@datalayer/core 1.1.66 → 1.2.10

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 (210) hide show
  1. package/README.md +41 -18
  2. package/lib/api/constants.d.ts +1 -1
  3. package/lib/api/constants.js +1 -1
  4. package/lib/api/contents/attachments.js +3 -7
  5. package/lib/api/contents/datasets.d.ts +48 -0
  6. package/lib/api/contents/datasets.js +44 -7
  7. package/lib/api/contents/runtimeMounts.js +1 -1
  8. package/lib/api/contents/sandboxUid.d.ts +13 -45
  9. package/lib/api/contents/sandboxUid.js +14 -65
  10. package/lib/api/evals/client.d.ts +402 -0
  11. package/lib/api/evals/client.js +359 -0
  12. package/lib/api/evals/index.d.ts +10 -0
  13. package/lib/api/evals/index.js +18 -0
  14. package/lib/api/evals/request.d.ts +43 -0
  15. package/lib/api/evals/request.js +67 -0
  16. package/lib/api/evals/status.d.ts +27 -0
  17. package/lib/api/evals/status.js +122 -0
  18. package/lib/api/evals/types.d.ts +1335 -0
  19. package/lib/api/evals/types.js +5 -0
  20. package/lib/api/iam/connectedAgents.d.ts +29 -0
  21. package/lib/api/iam/connectedAgents.js +35 -0
  22. package/lib/api/iam/identityProviders.d.ts +76 -0
  23. package/lib/api/iam/identityProviders.js +187 -0
  24. package/lib/api/iam/index.d.ts +1 -0
  25. package/lib/api/iam/index.js +1 -0
  26. package/lib/api/iam/mcpPolicy.d.ts +102 -1
  27. package/lib/api/iam/mcpPolicy.js +38 -2
  28. package/lib/api/iam/profile.d.ts +17 -1
  29. package/lib/api/iam/profile.js +27 -0
  30. package/lib/api/iam/scimTokens.d.ts +79 -0
  31. package/lib/api/iam/scimTokens.js +141 -0
  32. package/lib/api/iam/secrets.js +9 -21
  33. package/lib/api/iam/trials.d.ts +38 -0
  34. package/lib/api/iam/trials.js +66 -0
  35. package/lib/api/index.d.ts +4 -0
  36. package/lib/api/index.js +4 -0
  37. package/lib/api/mcp/observability.d.ts +25 -9
  38. package/lib/api/mcp/observability.js +63 -50
  39. package/lib/api/orchestration/client.d.ts +53 -0
  40. package/lib/api/orchestration/client.js +105 -0
  41. package/lib/api/orchestration/events.d.ts +88 -0
  42. package/lib/api/orchestration/events.js +245 -0
  43. package/lib/api/orchestration/generated.d.ts +693 -0
  44. package/lib/api/orchestration/generated.js +687 -0
  45. package/lib/api/orchestration/index.d.ts +30 -0
  46. package/lib/api/orchestration/index.js +34 -0
  47. package/lib/api/orchestration/lifecycle.d.ts +39 -0
  48. package/lib/api/orchestration/lifecycle.js +57 -0
  49. package/lib/api/orchestration/measures.d.ts +55 -0
  50. package/lib/api/orchestration/measures.js +99 -0
  51. package/lib/api/otel/dashboards.d.ts +56 -0
  52. package/lib/api/otel/dashboards.js +50 -0
  53. package/lib/api/otel/index.d.ts +1 -0
  54. package/lib/api/otel/index.js +1 -0
  55. package/lib/api/otel/metrics.d.ts +16 -0
  56. package/lib/api/otel/metrics.js +15 -0
  57. package/lib/api/scheduler/client.d.ts +32 -0
  58. package/lib/api/scheduler/client.js +42 -0
  59. package/lib/api/scheduler/index.d.ts +8 -0
  60. package/lib/api/scheduler/index.js +16 -0
  61. package/lib/api/scheduler/request.d.ts +28 -0
  62. package/lib/api/scheduler/request.js +50 -0
  63. package/lib/api/scheduler/types.d.ts +88 -0
  64. package/lib/api/scheduler/types.js +5 -0
  65. package/lib/api/spacer/comments.d.ts +97 -0
  66. package/lib/api/spacer/comments.js +45 -0
  67. package/lib/api/spacer/index.d.ts +9 -0
  68. package/lib/api/spacer/index.js +17 -0
  69. package/lib/api/spacer/notebooks.d.ts +33 -0
  70. package/lib/api/spacer/notebooks.js +39 -0
  71. package/lib/api/spacer/request.d.ts +25 -0
  72. package/lib/api/spacer/request.js +46 -0
  73. package/lib/api/spacer/spaces.d.ts +68 -0
  74. package/lib/api/spacer/spaces.js +45 -0
  75. package/lib/api/utils/validation.d.ts +4 -4
  76. package/lib/api/utils/validation.js +10 -7
  77. package/lib/client/constants.d.ts +1 -0
  78. package/lib/client/constants.js +1 -0
  79. package/lib/components/animation/AnimatedText.d.ts +1 -44
  80. package/lib/components/animation/AnimatedText.js +6 -122
  81. package/lib/components/anonymous/AnonymousKeyExpired.d.ts +56 -0
  82. package/lib/components/anonymous/AnonymousKeyExpired.js +96 -0
  83. package/lib/components/anonymous/AnonymousKeyTimer.d.ts +63 -0
  84. package/lib/components/anonymous/AnonymousKeyTimer.js +140 -0
  85. package/lib/components/anonymous/index.d.ts +7 -0
  86. package/lib/components/anonymous/index.js +15 -0
  87. package/lib/components/avatars/UserAvatar.d.ts +7 -1
  88. package/lib/components/avatars/UserAvatar.js +15 -1
  89. package/lib/components/billing/BillingEntitySelect.d.ts +6 -0
  90. package/lib/components/billing/BillingEntitySelect.js +49 -10
  91. package/lib/components/billing/eligibility.d.ts +22 -0
  92. package/lib/components/billing/eligibility.js +27 -0
  93. package/lib/components/billing/index.d.ts +1 -0
  94. package/lib/components/billing/index.js +1 -0
  95. package/lib/components/collaboration/LiveEditorCollaborators.d.ts +5 -0
  96. package/lib/components/collaboration/LiveEditorCollaborators.js +3 -1
  97. package/lib/components/display/DatalayerBox.d.ts +2 -3
  98. package/lib/components/display/DatalayerBox.js +1 -1
  99. package/lib/components/display/NavLink.js +3 -2
  100. package/lib/components/labels/StatusLabels.d.ts +2 -10
  101. package/lib/components/labels/StatusLabels.js +2 -26
  102. package/lib/components/principal/PrincipalAppearance.d.ts +21 -42
  103. package/lib/components/principal/PrincipalAppearance.js +232 -174
  104. package/lib/components/principal/PrincipalAvatar.d.ts +8 -1
  105. package/lib/components/principal/PrincipalAvatar.js +2 -2
  106. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +8 -1
  107. package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
  108. package/lib/components/screencapture/Screencapture.js +46 -3
  109. package/lib/components/sharing/ShareAccessComponent.d.ts +45 -4
  110. package/lib/components/sharing/ShareAccessComponent.js +110 -78
  111. package/lib/components/sharing/SharingEditor.d.ts +5 -1
  112. package/lib/components/sharing/SharingEditor.js +9 -4
  113. package/lib/components/sharing/index.d.ts +1 -0
  114. package/lib/components/sharing/index.js +1 -0
  115. package/lib/components/sharing/sandboxSharing.d.ts +26 -0
  116. package/lib/components/sharing/sandboxSharing.js +41 -0
  117. package/lib/components/spaces/SpaceDetailsOverlay.d.ts +39 -0
  118. package/lib/components/spaces/SpaceDetailsOverlay.js +80 -0
  119. package/lib/components/spaces/SpaceDisplay.d.ts +38 -0
  120. package/lib/components/spaces/SpaceDisplay.js +92 -0
  121. package/lib/components/spaces/index.d.ts +4 -0
  122. package/lib/components/spaces/index.js +7 -0
  123. package/lib/components/spaces/spaceDisplayModel.d.ts +93 -0
  124. package/lib/components/spaces/spaceDisplayModel.js +170 -0
  125. package/lib/hooks/cacheConverters.d.ts +0 -7
  126. package/lib/hooks/cacheConverters.js +0 -25
  127. package/lib/hooks/index.d.ts +1 -0
  128. package/lib/hooks/index.js +1 -0
  129. package/lib/hooks/useBillingEntityStore.js +3 -0
  130. package/lib/hooks/useCache.d.ts +168 -25
  131. package/lib/hooks/useCache.js +1424 -307
  132. package/lib/hooks/useContents.d.ts +22 -1
  133. package/lib/hooks/useContents.js +90 -21
  134. package/lib/hooks/useMcp.d.ts +87 -1
  135. package/lib/hooks/useMcp.js +247 -12
  136. package/lib/hooks/useNavigate.d.ts +41 -3
  137. package/lib/hooks/useNavigate.js +106 -52
  138. package/lib/hooks/usePrincipalCacheStore.js +3 -0
  139. package/lib/hooks/usePrincipalStore.js +3 -0
  140. package/lib/hooks/useSpaceCacheStore.d.ts +50 -0
  141. package/lib/hooks/useSpaceCacheStore.js +85 -0
  142. package/lib/models/CreditsDTO.d.ts +6 -5
  143. package/lib/models/CreditsDTO.js +7 -5
  144. package/lib/models/McpBinding.d.ts +6 -0
  145. package/lib/models/Organization.d.ts +69 -0
  146. package/lib/models/Organization.js +96 -0
  147. package/lib/models/Secret.d.ts +1 -2
  148. package/lib/models/Secret.js +2 -18
  149. package/lib/models/StartedBy.d.ts +34 -0
  150. package/lib/models/StartedBy.js +36 -0
  151. package/lib/models/Team.d.ts +2 -0
  152. package/lib/models/Team.js +2 -0
  153. package/lib/models/User.d.ts +4 -0
  154. package/lib/models/User.js +3 -0
  155. package/lib/models/UserSettings.d.ts +13 -0
  156. package/lib/models/UserSettings.js +9 -0
  157. package/lib/models/contents/__fixtures__/v1-contracts.json +158 -30
  158. package/lib/navigation/components.js +2 -3
  159. package/lib/routes/publicPaths.js +4 -0
  160. package/lib/state/index.d.ts +1 -0
  161. package/lib/state/index.js +1 -0
  162. package/lib/state/sessionEnd.d.ts +55 -0
  163. package/lib/state/sessionEnd.js +162 -0
  164. package/lib/state/substates/CoreState.js +3 -3
  165. package/lib/state/substates/IAMState.js +7 -0
  166. package/lib/state/substates/LayoutState.js +12 -0
  167. package/lib/state/substates/ProfileState.d.ts +1 -0
  168. package/lib/state/substates/ProfileState.js +1 -0
  169. package/lib/utils/Lazy.d.ts +20 -0
  170. package/lib/utils/Lazy.js +32 -6
  171. package/lib/utils/Screencapture.js +3 -0
  172. package/lib/utils/WithSuspense.d.ts +11 -1
  173. package/lib/utils/WithSuspense.js +11 -1
  174. package/lib/views/mcp/AdmittedClients.d.ts +44 -0
  175. package/lib/views/mcp/AdmittedClients.js +59 -0
  176. package/lib/views/mcp/ApprovalQueue.d.ts +66 -0
  177. package/lib/views/mcp/ApprovalQueue.js +95 -0
  178. package/lib/views/mcp/ConnectedAgents.js +38 -3
  179. package/lib/views/mcp/EnterpriseConsole.d.ts +1 -1
  180. package/lib/views/mcp/EnterpriseConsole.js +23 -1
  181. package/lib/views/mcp/IdentityProviders.d.ts +33 -0
  182. package/lib/views/mcp/IdentityProviders.js +299 -0
  183. package/lib/views/mcp/McpDashboard.js +90 -7
  184. package/lib/views/mcp/McpObservability.d.ts +30 -1
  185. package/lib/views/mcp/McpObservability.js +105 -19
  186. package/lib/views/mcp/NotebookRuns.d.ts +39 -0
  187. package/lib/views/mcp/NotebookRuns.js +44 -0
  188. package/lib/views/mcp/OrganizationPolicy.js +2 -1
  189. package/lib/views/mcp/PersonalPolicy.js +2 -1
  190. package/lib/views/mcp/PolicyForm.d.ts +17 -1
  191. package/lib/views/mcp/PolicyForm.js +25 -3
  192. package/lib/views/mcp/PolicyHistory.d.ts +13 -0
  193. package/lib/views/mcp/PolicyHistory.js +14 -1
  194. package/lib/views/mcp/RunDetail.d.ts +87 -0
  195. package/lib/views/mcp/RunDetail.js +186 -0
  196. package/lib/views/mcp/ScimProvisioning.d.ts +54 -0
  197. package/lib/views/mcp/ScimProvisioning.js +173 -0
  198. package/lib/views/mcp/TeamPolicies.js +66 -5
  199. package/lib/views/mcp/ToolAccess.d.ts +60 -0
  200. package/lib/views/mcp/ToolAccess.js +101 -0
  201. package/lib/views/mcp/TraceTimeline.d.ts +81 -0
  202. package/lib/views/mcp/TraceTimeline.js +132 -0
  203. package/lib/views/mcp/index.d.ts +5 -0
  204. package/lib/views/mcp/index.js +5 -0
  205. package/lib/views/otel/simpleAuthStore.js +3 -0
  206. package/lib/views/secrets/SecretEdit.js +3 -2
  207. package/lib/views/secrets/SecretNew.js +5 -1
  208. package/package.json +269 -264
  209. package/scripts/generate-mcp-types.py +1 -1
  210. package/scripts/generate-orchestration-types.py +920 -0
@@ -22,13 +22,15 @@
22
22
  import { useEffect } from 'react';
23
23
  import { useMutation, useQueries, useQuery, useQueryClient, } from '@tanstack/react-query';
24
24
  import { answerTask, cancelTask, exportAuditEvents, fetchMcpLogs, fetchMcpMetrics, fetchRunTrace, fetchTraceSpans, getEffectivePolicy, getGatewayVersion, getMcpActivity, getOrganizationMcpOverview, getOrganizationMcpUsage, getTask, getWorkflowsHealth, isMcpTaskTerminal, listAuditEvents, listBindings, listNotebookTasks, listTasks, listWorkers, subscribeTaskEvents, terminateBinding, } from '../api/mcp/index.js';
25
- import { disconnectAgent, listConnectedAgents } from '../api/iam/connectedAgents.js';
25
+ import { disconnectAgent, disconnectEveryAgent, disconnectEveryAgentInTeam, listConnectedAgents, } from '../api/iam/connectedAgents.js';
26
26
  import { listOrganizationTeams, } from '../api/iam/organizationTeams.js';
27
27
  import { getAuditSettings, setAuditSettings, } from '../api/iam/mcpAuditSettings.js';
28
- import { getMcpForwarding, } from '../api/mcp/forwarding.js';
29
- import { testAlertRule, } from '../api/mcp/alerts.js';
28
+ import { getMcpForwarding } from '../api/mcp/forwarding.js';
29
+ import { testAlertRule } from '../api/mcp/alerts.js';
30
30
  import { createAlertRule, deleteAlertRule, listAlertRules, updateAlertRule, } from '../api/iam/mcpAlertRules.js';
31
- import { deleteMcpPolicy, getMcpPolicy, setMcpPolicy, } from '../api/iam/mcpPolicy.js';
31
+ import { createIdentityProvider, deleteIdentityProvider, disableIdentityProvider, enableIdentityProvider, getDomainVerification, listIdentityProviders, updateIdentityProvider, verifyDomain, } from '../api/iam/identityProviders.js';
32
+ import { createScimToken, listScimTokens, revokeScimToken, rotateScimToken, } from '../api/iam/scimTokens.js';
33
+ import { deleteMcpPolicy, getAdmittedClients, getMcpPolicy, setMcpPolicy, } from '../api/iam/mcpPolicy.js';
32
34
  import { createServiceAgent, listServiceAgents, revokeServiceAgent, rotateServiceAgentKey, } from '../api/iam/serviceAgents.js';
33
35
  import { useCoreStore, useIAMStore } from '../state/index.js';
34
36
  import { queryKeys } from './useCache.js';
@@ -55,7 +57,9 @@ export const useTask = (taskUid) => {
55
57
  queryKey: queryKeys.mcp.task(taskUid ?? ''),
56
58
  queryFn: () => getTask(token ?? '', taskUid, mcpUrl),
57
59
  enabled: Boolean(token && mcpUrl && taskUid),
58
- refetchInterval: query => query.state.data && isMcpTaskTerminal(query.state.data.status) ? false : 2_000,
60
+ refetchInterval: query => query.state.data && isMcpTaskTerminal(query.state.data.status)
61
+ ? false
62
+ : 2_000,
59
63
  });
60
64
  };
61
65
  export const useNotebookTasks = (notebookUid, filters = {}) => {
@@ -97,13 +101,17 @@ export const useTaskEvents = (taskUid, options = {}) => {
97
101
  status: event.status ?? current.status,
98
102
  statusMessage: event.statusMessage ?? current.statusMessage,
99
103
  lastUpdatedAt: event.at ?? current.lastUpdatedAt,
100
- outputs: event.output ? [...(current.outputs ?? []), event.output] : current.outputs,
104
+ outputs: event.output
105
+ ? [...(current.outputs ?? []), event.output]
106
+ : current.outputs,
101
107
  };
102
108
  });
103
109
  onEvent?.(event);
104
110
  },
105
111
  onClose: () => {
106
- queryClient.invalidateQueries({ queryKey: queryKeys.mcp.task(taskUid) });
112
+ queryClient.invalidateQueries({
113
+ queryKey: queryKeys.mcp.task(taskUid),
114
+ });
107
115
  queryClient.invalidateQueries({ queryKey: queryKeys.mcp.tasks() });
108
116
  },
109
117
  }, mcpUrl);
@@ -305,7 +313,45 @@ export const useDisconnectAgent = () => {
305
313
  return useMutation({
306
314
  mutationFn: grantUid => disconnectAgent(token ?? '', grantUid, iamUrl),
307
315
  onSuccess: () => {
308
- queryClient.invalidateQueries({ queryKey: queryKeys.mcp.connectedAgents() });
316
+ queryClient.invalidateQueries({
317
+ queryKey: queryKeys.mcp.connectedAgents(),
318
+ });
319
+ queryClient.invalidateQueries({ queryKey: queryKeys.mcp.activity() });
320
+ },
321
+ });
322
+ };
323
+ /**
324
+ * Disconnect every agent of this person — and so force the next
325
+ * authorization through the consent screen.
326
+ *
327
+ * The grant *is* the consent, so there is no separate "ask again" to keep in
328
+ * step: ending them all is what forcing re-consent means.
329
+ */
330
+ export const useDisconnectEveryAgent = () => {
331
+ const queryClient = useQueryClient();
332
+ const token = useIAMStore(state => state.token);
333
+ const iamUrl = useIamUrl();
334
+ return useMutation({
335
+ mutationFn: () => disconnectEveryAgent(token ?? '', iamUrl),
336
+ onSuccess: () => {
337
+ queryClient.invalidateQueries({
338
+ queryKey: queryKeys.mcp.connectedAgents(),
339
+ });
340
+ queryClient.invalidateQueries({ queryKey: queryKeys.mcp.activity() });
341
+ },
342
+ });
343
+ };
344
+ /** The same for one team's agents, for whoever may manage that team. */
345
+ export const useDisconnectEveryAgentInTeam = () => {
346
+ const queryClient = useQueryClient();
347
+ const token = useIAMStore(state => state.token);
348
+ const iamUrl = useIamUrl();
349
+ return useMutation({
350
+ mutationFn: teamUid => disconnectEveryAgentInTeam(token ?? '', teamUid, iamUrl),
351
+ onSuccess: () => {
352
+ queryClient.invalidateQueries({
353
+ queryKey: queryKeys.mcp.connectedAgents(),
354
+ });
309
355
  queryClient.invalidateQueries({ queryKey: queryKeys.mcp.activity() });
310
356
  },
311
357
  });
@@ -364,7 +410,9 @@ export const useSetAuditSettings = (orgUid) => {
364
410
  return useMutation({
365
411
  mutationFn: ({ settings, expectedVersion }) => setAuditSettings(token ?? '', orgUid, settings, { expectedVersion }, iamUrl),
366
412
  onSuccess: () => {
367
- queryClient.invalidateQueries({ queryKey: queryKeys.mcp.auditSettings(orgUid) });
413
+ queryClient.invalidateQueries({
414
+ queryKey: queryKeys.mcp.auditSettings(orgUid),
415
+ });
368
416
  },
369
417
  });
370
418
  };
@@ -401,7 +449,9 @@ export const useCreateAlertRule = (orgUid) => {
401
449
  return useMutation({
402
450
  mutationFn: rule => createAlertRule(token ?? '', orgUid, rule, iamUrl),
403
451
  onSuccess: () => {
404
- queryClient.invalidateQueries({ queryKey: queryKeys.mcp.alertRules(orgUid) });
452
+ queryClient.invalidateQueries({
453
+ queryKey: queryKeys.mcp.alertRules(orgUid),
454
+ });
405
455
  },
406
456
  });
407
457
  };
@@ -412,7 +462,9 @@ export const useUpdateAlertRule = (orgUid) => {
412
462
  return useMutation({
413
463
  mutationFn: ({ uid, rule }) => updateAlertRule(token ?? '', orgUid, uid, rule, iamUrl),
414
464
  onSuccess: () => {
415
- queryClient.invalidateQueries({ queryKey: queryKeys.mcp.alertRules(orgUid) });
465
+ queryClient.invalidateQueries({
466
+ queryKey: queryKeys.mcp.alertRules(orgUid),
467
+ });
416
468
  },
417
469
  });
418
470
  };
@@ -423,7 +475,190 @@ export const useDeleteAlertRule = (orgUid) => {
423
475
  return useMutation({
424
476
  mutationFn: uid => deleteAlertRule(token ?? '', orgUid, uid, iamUrl),
425
477
  onSuccess: () => {
426
- queryClient.invalidateQueries({ queryKey: queryKeys.mcp.alertRules(orgUid) });
478
+ queryClient.invalidateQueries({
479
+ queryKey: queryKeys.mcp.alertRules(orgUid),
480
+ });
481
+ },
482
+ });
483
+ };
484
+ // -- Identity providers (IAM) -----------------------------------------------
485
+ /** One organization's identity providers, disabled ones included. */
486
+ export const useIdentityProviders = (orgUid, options = {}) => {
487
+ const token = useIAMStore(state => state.token);
488
+ const iamUrl = useIamUrl();
489
+ return useQuery({
490
+ queryKey: queryKeys.mcp.identityProviders(orgUid),
491
+ queryFn: () => listIdentityProviders(token ?? '', orgUid, iamUrl),
492
+ enabled: Boolean(token && iamUrl && orgUid) && (options.enabled ?? true),
493
+ staleTime: 30_000,
494
+ });
495
+ };
496
+ export const useCreateIdentityProvider = (orgUid) => {
497
+ const queryClient = useQueryClient();
498
+ const token = useIAMStore(state => state.token);
499
+ const iamUrl = useIamUrl();
500
+ return useMutation({
501
+ mutationFn: provider => createIdentityProvider(token ?? '', orgUid, provider, iamUrl),
502
+ onSuccess: () => {
503
+ queryClient.invalidateQueries({
504
+ queryKey: queryKeys.mcp.identityProviders(orgUid),
505
+ });
506
+ },
507
+ });
508
+ };
509
+ export const useUpdateIdentityProvider = (orgUid) => {
510
+ const queryClient = useQueryClient();
511
+ const token = useIAMStore(state => state.token);
512
+ const iamUrl = useIamUrl();
513
+ return useMutation({
514
+ mutationFn: ({ uid, provider }) => updateIdentityProvider(token ?? '', orgUid, uid, provider, iamUrl),
515
+ onSuccess: () => {
516
+ queryClient.invalidateQueries({
517
+ queryKey: queryKeys.mcp.identityProviders(orgUid),
518
+ });
519
+ },
520
+ });
521
+ };
522
+ export const useSetIdentityProviderEnabled = (orgUid) => {
523
+ const queryClient = useQueryClient();
524
+ const token = useIAMStore(state => state.token);
525
+ const iamUrl = useIamUrl();
526
+ return useMutation({
527
+ mutationFn: ({ uid, enabled }) => enabled
528
+ ? enableIdentityProvider(token ?? '', orgUid, uid, iamUrl)
529
+ : disableIdentityProvider(token ?? '', orgUid, uid, iamUrl),
530
+ onSuccess: () => {
531
+ queryClient.invalidateQueries({
532
+ queryKey: queryKeys.mcp.identityProviders(orgUid),
533
+ });
534
+ },
535
+ });
536
+ };
537
+ /**
538
+ * What one layer's `allowedClients` entries actually name.
539
+ *
540
+ * Deliberately *not* folded into `useMcpPolicyLayer`. That query is read
541
+ * whenever a policy form mounts; this one may make the server fetch a client
542
+ * document per entry, and an editor typing in a textarea must not set that
543
+ * off on every keystroke — so it is fetched from what is **stored**, and goes
544
+ * stale until the draft is saved.
545
+ */
546
+ export const useAdmittedClients = (scope, subjectUid, options = {}) => {
547
+ const token = useIAMStore(state => state.token);
548
+ const iamUrl = useIamUrl();
549
+ return useQuery({
550
+ queryKey: queryKeys.mcp.admittedClients(scope, subjectUid),
551
+ queryFn: () => getAdmittedClients(token ?? '', scope, subjectUid, iamUrl),
552
+ enabled: Boolean(token && iamUrl && subjectUid) && (options.enabled ?? true),
553
+ // Longer than the policy layer's: a client document is cached by the
554
+ // server for as long as its own headers say, and re-asking on every
555
+ // focus would make a console page a source of outbound traffic.
556
+ staleTime: 300_000,
557
+ });
558
+ };
559
+ // -- SCIM provisioning credentials (IAM) ------------------------------------
560
+ /**
561
+ * One organization's SCIM tokens, revoked ones included.
562
+ *
563
+ * Revoked ones are *not* filtered out here. A console that hid them would
564
+ * make an organization whose every token is revoked look identical to one
565
+ * that never had any — and those are different problems with different
566
+ * fixes, only one of which is "nobody is being deprovisioned right now".
567
+ */
568
+ export const useScimTokens = (orgUid, options = {}) => {
569
+ const token = useIAMStore(state => state.token);
570
+ const iamUrl = useIamUrl();
571
+ return useQuery({
572
+ queryKey: queryKeys.mcp.scimTokens(orgUid),
573
+ queryFn: () => listScimTokens(token ?? '', orgUid, iamUrl),
574
+ enabled: Boolean(token && iamUrl && orgUid) && (options.enabled ?? true),
575
+ staleTime: 30_000,
576
+ });
577
+ };
578
+ /**
579
+ * Mint one. The secret is in the result and nowhere else, ever again.
580
+ *
581
+ * The mutation result is deliberately not written into the query cache: a
582
+ * secret that lived in a cache would be re-rendered on every refetch of the
583
+ * list, and outlive the dialog it was shown in.
584
+ */
585
+ export const useCreateScimToken = (orgUid) => {
586
+ const queryClient = useQueryClient();
587
+ const token = useIAMStore(state => state.token);
588
+ const iamUrl = useIamUrl();
589
+ return useMutation({
590
+ mutationFn: name => createScimToken(token ?? '', orgUid, name, iamUrl),
591
+ onSuccess: () => {
592
+ queryClient.invalidateQueries({
593
+ queryKey: queryKeys.mcp.scimTokens(orgUid),
594
+ });
595
+ },
596
+ });
597
+ };
598
+ export const useRotateScimToken = (orgUid) => {
599
+ const queryClient = useQueryClient();
600
+ const token = useIAMStore(state => state.token);
601
+ const iamUrl = useIamUrl();
602
+ return useMutation({
603
+ mutationFn: uid => rotateScimToken(token ?? '', orgUid, uid, iamUrl),
604
+ onSuccess: () => {
605
+ queryClient.invalidateQueries({
606
+ queryKey: queryKeys.mcp.scimTokens(orgUid),
607
+ });
608
+ },
609
+ });
610
+ };
611
+ export const useRevokeScimToken = (orgUid) => {
612
+ const queryClient = useQueryClient();
613
+ const token = useIAMStore(state => state.token);
614
+ const iamUrl = useIamUrl();
615
+ return useMutation({
616
+ mutationFn: uid => revokeScimToken(token ?? '', orgUid, uid, iamUrl),
617
+ onSuccess: () => {
618
+ queryClient.invalidateQueries({
619
+ queryKey: queryKeys.mcp.scimTokens(orgUid),
620
+ });
621
+ },
622
+ });
623
+ };
624
+ export const useDeleteIdentityProvider = (orgUid) => {
625
+ const queryClient = useQueryClient();
626
+ const token = useIAMStore(state => state.token);
627
+ const iamUrl = useIamUrl();
628
+ return useMutation({
629
+ mutationFn: uid => deleteIdentityProvider(token ?? '', orgUid, uid, iamUrl),
630
+ onSuccess: () => {
631
+ queryClient.invalidateQueries({
632
+ queryKey: queryKeys.mcp.identityProviders(orgUid),
633
+ });
634
+ },
635
+ });
636
+ };
637
+ /**
638
+ * What to publish in DNS, fetched on demand rather than kept fresh.
639
+ *
640
+ * A mutation, not a query with a `refetch`: this is asked for once when
641
+ * somebody opens the verification panel, and the value never changes for a
642
+ * given provider and domain — what changes is `verified`, which the
643
+ * `verify` call below answers directly from the provider it returns.
644
+ */
645
+ export const useDomainVerification = (orgUid) => {
646
+ const token = useIAMStore(state => state.token);
647
+ const iamUrl = useIamUrl();
648
+ return useMutation({
649
+ mutationFn: ({ uid, domain }) => getDomainVerification(token ?? '', orgUid, uid, domain, iamUrl),
650
+ });
651
+ };
652
+ export const useVerifyDomain = (orgUid) => {
653
+ const queryClient = useQueryClient();
654
+ const token = useIAMStore(state => state.token);
655
+ const iamUrl = useIamUrl();
656
+ return useMutation({
657
+ mutationFn: ({ uid, domain }) => verifyDomain(token ?? '', orgUid, uid, domain, iamUrl),
658
+ onSuccess: () => {
659
+ queryClient.invalidateQueries({
660
+ queryKey: queryKeys.mcp.identityProviders(orgUid),
661
+ });
427
662
  },
428
663
  });
429
664
  };
@@ -1,6 +1,44 @@
1
+ export interface NavigateOptions {
2
+ /** Replace the current history entry instead of pushing one. */
3
+ replace?: boolean;
4
+ /** History state to carry to the destination. */
5
+ state?: unknown;
6
+ /** Open the destination in a new tab and leave this one where it is. */
7
+ newTab?: boolean;
8
+ /** Scroll to the top on arrival; on by default. */
9
+ scroll?: boolean;
10
+ /**
11
+ * Close the layout's side portals on the way; on by default. Only the
12
+ * history API host has them.
13
+ */
14
+ resetPortals?: boolean;
15
+ }
1
16
  /**
2
- * Main navigation hook that provides a universal navigate function
3
- * Works with React Router, Next.js, or native browser navigation
17
+ * The part of a click (or a key press, from a menu) the hook looks at. A
18
+ * structural type, so React's synthetic events and DOM events both fit.
4
19
  */
5
- export declare const useNavigate: () => (location: string | number, optionsOrEvent?: any, resetPortals?: boolean, extraOptions?: any) => void;
20
+ export interface NavigateEvent {
21
+ preventDefault: () => void;
22
+ metaKey?: boolean;
23
+ ctrlKey?: boolean;
24
+ shiftKey?: boolean;
25
+ altKey?: boolean;
26
+ button?: number;
27
+ currentTarget?: unknown;
28
+ target?: unknown;
29
+ }
30
+ export type NavigateTarget = string | number;
31
+ /**
32
+ * The call shapes accepted, old and new:
33
+ * - `navigate(to, options?)`
34
+ * - `navigate(to, event, options?)`
35
+ * - `navigate(to, event, resetPortals, options?)` (legacy)
36
+ * - `navigate(to, resetPortals, options?)` (legacy)
37
+ */
38
+ export type Navigate = (to: NavigateTarget, optionsOrEvent?: NavigateOptions | NavigateEvent | boolean, third?: boolean | NavigateOptions, fourth?: NavigateOptions) => void;
39
+ /** Whether `event` is a click meant to open a new tab or window. */
40
+ export declare function isNewTabClick(event: NavigateEvent): boolean;
41
+ /** Open `to` in a new tab, without handing it this window. */
42
+ export declare function openInNewTab(to: string): void;
43
+ export declare const useNavigate: () => Navigate;
6
44
  export default useNavigate;
@@ -2,88 +2,142 @@
2
2
  * Copyright (c) 2023-2025 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- import { useCallback } from 'react';
5
+ /**
6
+ * Navigation for any host: React Router when the app runs under one, the
7
+ * history API otherwise.
8
+ *
9
+ * One function, `navigate(to, …)`, with the click event as an optional second
10
+ * argument. Pass it and a click behaves the way a browser link does: a plain
11
+ * click navigates in place and stops the anchor's own navigation; a modifier
12
+ * click (⌘, Ctrl, Shift, Alt) or a middle click opens the target in a new tab
13
+ * — through the browser when the click was on a link to that target, through
14
+ * `window.open` otherwise, so a button gets it too. `newTab` in the options
15
+ * opens a new tab outright.
16
+ *
17
+ * @module hooks/useNavigate
18
+ */
19
+ import { useCallback, useMemo } from 'react';
6
20
  import { useLayoutStore } from '../state/index.js';
7
21
  import { createNativeNavigate } from '../navigation/adapters/native.js';
8
22
  // Import React Router hooks from our wrapper
9
23
  import { useNavigateRR } from '../navigation/adapters/react-router.js';
10
- // Import Next.js hooks from our wrapper
11
- // Currently not used but kept for future Next.js support
12
- // import { useRouterNext } from '../navigation/adapters/nextjs.js';
24
+ /** Whether `event` is a click meant to open a new tab or window. */
25
+ export function isNewTabClick(event) {
26
+ return (!!event.metaKey ||
27
+ !!event.ctrlKey ||
28
+ !!event.shiftKey ||
29
+ !!event.altKey ||
30
+ event.button === 1);
31
+ }
32
+ /** The absolute URL `to` names from the current page. */
33
+ const resolveUrl = (to) => new URL(to, window.location.href).href;
34
+ /** Open `to` in a new tab, without handing it this window. */
35
+ export function openInNewTab(to) {
36
+ window.open(resolveUrl(to), '_blank', 'noopener,noreferrer');
37
+ }
13
38
  /**
14
- * Main navigation hook that provides a universal navigate function
15
- * Works with React Router, Next.js, or native browser navigation
39
+ * Whether `event` happened on a link whose `href` already is `to`: then the
40
+ * browser opens the new tab itself, and nothing needs doing.
16
41
  */
42
+ function isLinkTo(event, to) {
43
+ const origin = (event.currentTarget ?? event.target);
44
+ const anchor = origin && typeof origin.closest === 'function'
45
+ ? origin.closest('a[href]')
46
+ : null;
47
+ if (!anchor) {
48
+ return false;
49
+ }
50
+ try {
51
+ return resolveUrl(anchor.getAttribute('href') ?? '') === resolveUrl(to);
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ }
57
+ const isEvent = (value) => !!value && typeof value.preventDefault === 'function';
58
+ function parseCall(optionsOrEvent, third, fourth) {
59
+ if (isEvent(optionsOrEvent)) {
60
+ const options = typeof third === 'boolean'
61
+ ? { resetPortals: third, ...(fourth ?? {}) }
62
+ : { ...(third ?? {}), ...(fourth ?? {}) };
63
+ return { event: optionsOrEvent, options };
64
+ }
65
+ if (typeof optionsOrEvent === 'boolean') {
66
+ return {
67
+ options: {
68
+ resetPortals: optionsOrEvent,
69
+ ...(typeof third === 'object' ? third : {}),
70
+ ...(fourth ?? {}),
71
+ },
72
+ };
73
+ }
74
+ return { options: { ...(optionsOrEvent ?? {}) } };
75
+ }
17
76
  export const useNavigate = () => {
18
77
  const layoutStore = useLayoutStore();
19
78
  // Detect environment
20
79
  const isNextJs = typeof window !== 'undefined' && !!window.__NEXT_DATA__;
21
80
  const isClient = typeof window !== 'undefined';
22
- // Try to use React Router's useNavigate if available
81
+ // React Router's navigate when the app runs under a Router, else nothing.
23
82
  let rrNavigate = null;
24
- let isReactRouter = false;
25
83
  if (!isNextJs && useNavigateRR && isClient) {
26
84
  try {
27
85
  // eslint-disable-next-line react-hooks/rules-of-hooks
28
86
  rrNavigate = useNavigateRR();
29
- isReactRouter = !!rrNavigate;
30
87
  }
31
88
  catch {
32
89
  // Not in a Router context
33
90
  }
34
91
  }
35
- // If React Router is available, return it directly (wrapped for consistency)
36
- if (isReactRouter && rrNavigate) {
37
- // eslint-disable-next-line react-hooks/rules-of-hooks
38
- return useCallback((to, options) => {
39
- // Scroll to top when navigating (except for history navigation)
40
- if (typeof to === 'string') {
41
- window.scrollTo(0, 0);
42
- document.body.scrollTop = 0;
92
+ const nativeNavigate = useMemo(() => createNativeNavigate(), []);
93
+ return useCallback((to, optionsOrEvent, third, fourth) => {
94
+ if (typeof window === 'undefined') {
95
+ return;
96
+ }
97
+ const { event, options } = parseCall(optionsOrEvent, third, fourth);
98
+ // History navigation (e.g. -1 to go back).
99
+ if (typeof to === 'number') {
100
+ event?.preventDefault();
101
+ if (rrNavigate) {
102
+ rrNavigate(to);
103
+ }
104
+ else {
105
+ window.history.go(to);
43
106
  }
44
- // For React Router, pass through directly
45
- return rrNavigate(to, options);
46
- }, [rrNavigate]);
47
- }
48
- // Otherwise use native navigation with our custom behavior
49
- const baseNavigate = createNativeNavigate();
50
- // Wrap with our custom behavior for native navigation
51
- // eslint-disable-next-line react-hooks/rules-of-hooks
52
- const navigate = useCallback((location, optionsOrEvent, resetPortals = true, extraOptions) => {
53
- // Handle number for history navigation (e.g., -1 to go back)
54
- if (typeof location === 'number') {
55
- window.history.go(location);
56
107
  return;
57
108
  }
58
- // Handle different call signatures for native navigation
59
- let options = undefined;
60
- let event = undefined;
61
- if (optionsOrEvent && optionsOrEvent.preventDefault) {
62
- // Legacy signature with event as second parameter
63
- event = optionsOrEvent;
64
- options = extraOptions;
109
+ // A new tab was asked for with the click. A link to that very place
110
+ // gets it from the browser; a button, or a link elsewhere, from us.
111
+ if (event && isNewTabClick(event)) {
112
+ if (!isLinkTo(event, to)) {
113
+ event.preventDefault();
114
+ openInNewTab(to);
115
+ }
116
+ return;
65
117
  }
66
- else if (typeof optionsOrEvent === 'boolean') {
67
- // Legacy signature with resetPortals as second parameter
68
- resetPortals = optionsOrEvent;
69
- options = extraOptions;
118
+ if (options.newTab) {
119
+ event?.preventDefault();
120
+ openInNewTab(to);
121
+ return;
70
122
  }
71
- else {
72
- // Standard signature with options as second parameter
73
- options = optionsOrEvent;
123
+ event?.preventDefault();
124
+ if (options.scroll !== false) {
125
+ window.scrollTo(0, 0);
126
+ document.body.scrollTop = 0;
74
127
  }
75
- if (event) {
76
- event.preventDefault();
128
+ if (rrNavigate) {
129
+ rrNavigate(to, { replace: options.replace, state: options.state });
130
+ return;
77
131
  }
78
- if (resetPortals) {
132
+ if (options.resetPortals !== false) {
79
133
  layoutStore.resetLeftPortal();
80
134
  layoutStore.resetRightPortal();
81
135
  }
82
- window.scrollTo(0, 0);
83
- document.body.scrollTop = 0;
84
- // Use native navigation
85
- baseNavigate(location, options);
86
- }, [baseNavigate, layoutStore]);
87
- return navigate;
136
+ nativeNavigate(to, {
137
+ replace: options.replace,
138
+ state: options.state,
139
+ scroll: options.scroll,
140
+ });
141
+ }, [rrNavigate, nativeNavigate, layoutStore]);
88
142
  };
89
143
  export default useNavigate;
@@ -3,6 +3,7 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import { create } from 'zustand';
6
+ import { registerSessionState } from '../state/sessionEnd.js';
6
7
  /** Build the cache key for a principal. */
7
8
  export function principalCacheKey(kind, uid) {
8
9
  return `${kind}:${String(uid || '').trim()}`;
@@ -94,3 +95,5 @@ export const usePrincipalCacheStore = create((set, get) => ({
94
95
  reset: () => set({ principals: {} }),
95
96
  }));
96
97
  export default usePrincipalCacheStore;
98
+ // What one session resolved is not the next one's to paint.
99
+ registerSessionState({ forget: () => usePrincipalCacheStore.getState().reset() });
@@ -5,6 +5,7 @@
5
5
  import { create } from 'zustand';
6
6
  import { createJSONStorage, persist } from 'zustand/middleware';
7
7
  import { useUsageRefreshStore } from './useUsageRefreshStore.js';
8
+ import { registerSessionState } from '../state/sessionEnd.js';
8
9
  const PRINCIPAL_CONTEXT_COOKIE = 'datalayer-principal-context';
9
10
  const PRINCIPAL_CONTEXT_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
10
11
  const readPrincipalContextCookie = () => {
@@ -182,3 +183,5 @@ export const usePrincipalStore = create()(persist(set => ({
182
183
  },
183
184
  }));
184
185
  export default usePrincipalStore;
186
+ // The principal selected is the signed-in person's: forgotten when the session ends.
187
+ registerSessionState({ forget: () => usePrincipalStore.getState().resetPrincipal() });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * A resolved space, kept in memory.
3
+ *
4
+ * The companion of {@link usePrincipalCacheStore}, for the same reason: a
5
+ * table of a hundred notebooks sitting in three spaces should resolve the
6
+ * three names once, and the next view that shows one of those spaces should
7
+ * paint it before any request has returned. Partial snapshots merge — a blank
8
+ * never overwrites a value already known — so what is known of a space
9
+ * accumulates across the views that saw it.
10
+ */
11
+ export type CachedSpace = {
12
+ /** The space uid: what the spacer API names a space by. */
13
+ uid: string;
14
+ /**
15
+ * The space's internal Solr id. An item is a nested child of its space and
16
+ * names it only by this (`_root_`), so a row that knows nothing else can
17
+ * still be matched to its space.
18
+ */
19
+ rootId?: string;
20
+ handle?: string;
21
+ displayName?: string;
22
+ description?: string;
23
+ variant?: string;
24
+ isPublic?: boolean;
25
+ /** The account the space is addressed under: its organization, else its owner. */
26
+ accountHandle?: string;
27
+ /** Timestamp (ms) of the last time this entry changed. */
28
+ updatedAt?: number;
29
+ };
30
+ type SpaceCacheState = {
31
+ /** Cached spaces keyed by uid. */
32
+ spaces: Record<string, CachedSpace>;
33
+ /** Space uids keyed by the Solr id items carry as `_root_`. */
34
+ uidByRootId: Record<string, string>;
35
+ /** Read a cached space by uid, if present. */
36
+ getSpace: (uid: string) => CachedSpace | undefined;
37
+ /** Read a cached space by the Solr id an item names it by, if present. */
38
+ getSpaceByRootId: (rootId: string) => CachedSpace | undefined;
39
+ /**
40
+ * Merge a (possibly partial) space snapshot into the cache. Existing
41
+ * meaningful values are kept when the incoming snapshot omits them.
42
+ */
43
+ upsertSpace: (entry: CachedSpace) => void;
44
+ /** Remove a single cached space (e.g. after it was renamed or deleted). */
45
+ clearSpace: (uid: string) => void;
46
+ /** Clear the entire cache. */
47
+ reset: () => void;
48
+ };
49
+ export declare const useSpaceCacheStore: import("zustand").UseBoundStore<import("zustand").StoreApi<SpaceCacheState>>;
50
+ export default useSpaceCacheStore;