@sanity/sdk-react 3.4.0-rc.0 → 3.5.0-next.20260922182803

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 (41) hide show
  1. package/dist/_exports/dashboard.d.ts +151 -45
  2. package/dist/_exports/dashboard.d.ts.map +1 -1
  3. package/dist/_exports/dashboard.js +267 -125
  4. package/dist/_exports/dashboard.js.map +1 -1
  5. package/dist/index.d.ts +21 -141
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +66 -147
  8. package/dist/index.js.map +1 -1
  9. package/dist/{useStudioWorkspacesByProjectIdDataset-B5A5kBH9.js → useStudioWorkspacesByProjectIdDataset-DL6_9jgU.js} +61 -4
  10. package/dist/useStudioWorkspacesByProjectIdDataset-DL6_9jgU.js.map +1 -0
  11. package/package.json +9 -9
  12. package/src/_exports/dashboard.test-d.ts +3 -0
  13. package/src/_exports/dashboard.ts +13 -2
  14. package/src/_exports/sdk-react.ts +0 -3
  15. package/src/components/auth/AuthBoundary.test.tsx +2 -81
  16. package/src/components/auth/AuthBoundary.tsx +3 -17
  17. package/src/components/auth/LoginCallback.test.tsx +7 -46
  18. package/src/components/auth/LoginCallback.tsx +4 -22
  19. package/src/hooks/dashboard/useAgentResourceContext.test.tsx +67 -2
  20. package/src/hooks/dashboard/useAgentResourceContext.ts +35 -6
  21. package/src/hooks/dashboard/useApplicationContext.test.tsx +95 -0
  22. package/src/hooks/dashboard/useApplicationContext.ts +47 -0
  23. package/src/hooks/dashboard/useApplications.test.tsx +165 -7
  24. package/src/hooks/dashboard/useApplications.ts +112 -28
  25. package/src/hooks/dashboard/useCapabilities.test.tsx +84 -0
  26. package/src/hooks/dashboard/useCapabilities.ts +27 -0
  27. package/src/hooks/dashboard/useNavigate.test.ts +392 -5
  28. package/src/hooks/dashboard/useNavigate.ts +169 -26
  29. package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +116 -9
  30. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +110 -39
  31. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.test.ts +99 -1
  32. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.ts +73 -2
  33. package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.test.tsx +23 -1
  34. package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.ts +2 -1
  35. package/dist/useStudioWorkspacesByProjectIdDataset-B5A5kBH9.js.map +0 -1
  36. package/src/hooks/auth/useHandleOAuthCallback.test.tsx +0 -16
  37. package/src/hooks/auth/useHandleOAuthCallback.tsx +0 -49
  38. package/src/hooks/auth/useOAuthAuthorize.test.tsx +0 -16
  39. package/src/hooks/auth/useOAuthAuthorize.tsx +0 -28
  40. package/src/hooks/auth/useOAuthTokens.test.tsx +0 -240
  41. package/src/hooks/auth/useOAuthTokens.tsx +0 -95
@@ -1,12 +1,12 @@
1
- import { a as useWindowConnection, c as useSanityInstance, i as DashboardTokenRefreshProvider, n as useTopic, r as useOrganizationId, s as createStateSourceHook, t as useStudioWorkspacesByProjectIdDataset } from "../useStudioWorkspacesByProjectIdDataset-B5A5kBH9.js";
1
+ import { a as DashboardTokenRefreshProvider, c as createStateSourceHook, i as useOrganizationId, l as useSanityInstance, n as useTopic, o as useWindowConnection, r as useEmit, t as useStudioWorkspacesByProjectIdDataset } from "../useStudioWorkspacesByProjectIdDataset-DL6_9jgU.js";
2
2
  import { c } from "react-compiler-runtime";
3
3
  import "@sanity/sdk";
4
4
  import { createLogger, getApplicationOrigin, isDashboardEnvironment, requireDashboardMessageBus } from "@sanity/sdk/_internal";
5
- import { useEffect, useRef, useState } from "react";
5
+ import { useCallback, useEffect, useEffectEvent, useRef, useState } from "react";
6
6
  import { SDK_CHANNEL_NAME, SDK_NODE_NAME } from "@sanity/message-protocol";
7
7
  import "@sanity/sdk/comlink";
8
- import { of } from "rxjs";
9
- import { MessageBusError, TopicError, connectMessageBus } from "@sanity/sdk/dashboard";
8
+ import { filter, map, of, pairwise } from "rxjs";
9
+ import { MessageBusError, TopicError, TopicError as TopicError$1, capabilities, connectMessageBus } from "@sanity/sdk/dashboard";
10
10
  import "@sanity/types";
11
11
  import { createInstance } from "@module-federation/runtime";
12
12
  import "@module-federation/runtime/types";
@@ -382,13 +382,53 @@ const createRootBuilder = (Builder, ...segments) => new Builder(appendSegments(n
382
382
  return Object.assign(urls, methods);
383
383
  }, urlFor = createUrls({});
384
384
  /**
385
+ * Publishes what this application is currently showing to the Dashboard message bus, so the host
386
+ * can hold the foreground application's context. Pass `null` when nothing is open.
387
+ *
388
+ * The context is published on mount and whenever it changes, and cleared with `null` on unmount.
389
+ * A change publishes `null` before the new context, as the component has stopped showing the old
390
+ * one. Pass a memoised value: the hook compares by reference, so a new object on every render
391
+ * republishes on every render.
392
+ *
393
+ * The host reads the sending application from `message.meta.appId`, so the payload carries no
394
+ * application ID.
395
+ *
396
+ * @param context - The application context to publish, or `null` to clear it
397
+ *
398
+ * @example
399
+ * ```tsx
400
+ * import {useApplicationContext} from '@sanity/sdk-react/dashboard'
401
+ * import {useMemo} from 'react'
402
+ *
403
+ * function DocumentView({documentId}: {documentId: string}) {
404
+ * const context = useMemo(
405
+ * () => ({resource: {id: 'my-project.production', type: 'dataset' as const}, document: {id: documentId}}),
406
+ * [documentId],
407
+ * )
408
+ * useApplicationContext(context)
409
+ * return <div>Editing {documentId}</div>
410
+ * }
411
+ * ```
412
+ *
385
413
  * @public
386
- * Hook for emitting agent resource context updates to the Dashboard.
387
- * This allows the Agent to understand what resource the user is currently
388
- * interacting with (e.g., which document they're editing).
414
+ * @category Dashboard
415
+ */
416
+ function useApplicationContext(context) {
417
+ let $ = c(4), emit = useEmit("applications.context.update"), t0, t1;
418
+ $[0] !== context || $[1] !== emit ? (t0 = () => (emit(context), () => {
419
+ emit(null);
420
+ }), t1 = [emit, context], $[0] = context, $[1] = emit, $[2] = t0, $[3] = t1) : (t0 = $[2], t1 = $[3]), useEffect(t0, t1);
421
+ }
422
+ /**
423
+ * @public
424
+ * Hook for reporting the resource the user is currently interacting with (for example, which
425
+ * document they're editing) so the Agent can understand their context. The hook reports on mount
426
+ * and again whenever the context changes.
389
427
  *
390
- * The hook will automatically emit the context when it changes, and also
391
- * emit the initial context when the hook is first mounted.
428
+ * The two Dashboard runtimes differ in transport, but the signature is the same:
429
+ * - In an iframe (Comlink), it emits the `dashboard/v1/events/agent/resource/update` event.
430
+ * - In a federated app (message bus), it publishes {@link useApplicationContext}'s
431
+ * `applications.context.update` topic. Reach for `useApplicationContext` directly in new code.
392
432
  *
393
433
  * @category Agent
394
434
  * @param options - The resource context options containing projectId, dataset, and optional documentId
@@ -412,6 +452,19 @@ const createRootBuilder = (Builder, ...segments) => new Builder(appendSegments(n
412
452
  * ```
413
453
  */
414
454
  function useAgentResourceContext(options) {
455
+ return isDashboardEnvironment() ? useBusAgentResourceContext(options) : useComlinkAgentResourceContext(options);
456
+ }
457
+ function useBusAgentResourceContext(t0) {
458
+ let $ = c(4), { projectId, dataset, documentId } = t0, t1;
459
+ $[0] !== dataset || $[1] !== documentId || $[2] !== projectId ? (t1 = projectId && dataset ? {
460
+ resource: {
461
+ id: `${projectId}.${dataset}`,
462
+ type: "dataset"
463
+ },
464
+ document: documentId ? { id: documentId } : null
465
+ } : null, $[0] = dataset, $[1] = documentId, $[2] = projectId, $[3] = t1) : t1 = $[3], useApplicationContext(t1);
466
+ }
467
+ function useComlinkAgentResourceContext(options) {
415
468
  let $ = c(9), { projectId, dataset, documentId } = options, t0;
416
469
  $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = {
417
470
  name: SDK_NODE_NAME,
@@ -446,10 +499,9 @@ function useAgentResourceContext(options) {
446
499
  updateContext();
447
500
  }, t3 = [updateContext], $[6] = updateContext, $[7] = t2, $[8] = t3), useEffect(t2, t3);
448
501
  }
449
- const loadableInterfaces = ({ activeDeployment, config }) => config?.mfManifest === void 0 ? [] : activeDeployment?.interfaces ?? [], toApplication = (application) => {
450
- let { activeDeployment: _activeDeployment, config: _config, ...applicationBase } = application, interfaces = loadableInterfaces(application), entry = interfaces.length === 0 ? null : getApplicationOrigin(applicationBase);
502
+ const loadableInterfaces = ({ activeDeployment, config }, isLocal) => isLocal || config?.mfManifest !== void 0 ? activeDeployment?.interfaces ?? [] : [], loadModules = (base, interfaces) => {
503
+ let entry = interfaces.length === 0 ? null : getApplicationOrigin(base);
451
504
  if (entry === null) return {
452
- ...application,
453
505
  views: [],
454
506
  webWorkers: []
455
507
  };
@@ -457,13 +509,13 @@ const loadableInterfaces = ({ activeDeployment, config }) => config?.mfManifest
457
509
  for (let extension of interfaces) {
458
510
  let module = {
459
511
  entry,
460
- moduleId: `${applicationBase.id}/${extension.moduleId}`,
512
+ moduleId: `${base.id}/${extension.moduleId}`,
461
513
  version: extension.version
462
514
  };
463
515
  if (extension.type === "worker") {
464
516
  webWorkers.push({
465
517
  ...extension,
466
- application: applicationBase,
518
+ application: base,
467
519
  module
468
520
  });
469
521
  continue;
@@ -471,22 +523,57 @@ const loadableInterfaces = ({ activeDeployment, config }) => config?.mfManifest
471
523
  let { type, ...view } = extension;
472
524
  views.push({
473
525
  ...view,
474
- application: applicationBase,
526
+ application: base,
475
527
  module,
476
528
  surface: type === "app" ? "window" : type
477
529
  });
478
530
  }
479
531
  return {
480
- ...application,
481
532
  views,
482
533
  webWorkers
483
534
  };
535
+ }, toApplication = (application) => {
536
+ let { activeDeployment: _activeDeployment, config: _config, ...applicationBase } = application, isLocal = "local" in application;
537
+ return {
538
+ ...application,
539
+ isLocal,
540
+ ...loadModules({
541
+ ...applicationBase,
542
+ isLocal
543
+ }, loadableInterfaces(application, isLocal))
544
+ };
545
+ }, toInstallation = (installation) => {
546
+ let { application } = installation, base = {
547
+ id: installation.id,
548
+ type: "installation",
549
+ isLocal: !1,
550
+ title: application.title,
551
+ name: application.name,
552
+ reference: application.reference,
553
+ icon: application.icon,
554
+ isSingleton: !0,
555
+ visibility: "default",
556
+ slug: application.slug,
557
+ externalUrl: null,
558
+ organizationId: application.organizationId,
559
+ createdAt: installation.createdAt,
560
+ updatedAt: installation.updatedAt
561
+ };
562
+ return {
563
+ ...base,
564
+ type: "installation",
565
+ installation,
566
+ ...loadModules(base, installation.interfaces ?? [])
567
+ };
484
568
  };
485
569
  /**
486
- * Returns the applications available in the dashboard.
570
+ * Returns the applications and installations available in the dashboard, in one consistent shape.
487
571
  *
488
- * Suspends until the dashboard publishes its application list; a cleared list is empty. Throws a
489
- * `TopicError` to the nearest error boundary when the dashboard fails to load applications.
572
+ * Suspends until the dashboard publishes its list; a cleared list is empty. Throws a `TopicError`
573
+ * to the nearest error boundary when the dashboard fails to load. Every entry exposes the same base
574
+ * fields plus `views` and `webWorkers`; `type` distinguishes applications (`'studio' | 'coreApp'`)
575
+ * from installations (`'installation'`), which also carry the raw record under `installation`.
576
+ * `isLocal` marks applications served by a CLI dev server.
490
577
  *
491
578
  * @example
492
579
  * ```tsx
@@ -500,7 +587,10 @@ const loadableInterfaces = ({ activeDeployment, config }) => config?.mfManifest
500
587
  */
501
588
  function useApplications() {
502
589
  let $ = c(2), applications = useTopic("applications.list"), t0;
503
- return $[0] === applications ? t0 = $[1] : (t0 = applications?.map(toApplication) ?? [], $[0] = applications, $[1] = t0), t0;
590
+ return $[0] === applications ? t0 = $[1] : (t0 = applications?.map(_temp$2) ?? [], $[0] = applications, $[1] = t0), t0;
591
+ }
592
+ function _temp$2(entry) {
593
+ return "type" in entry ? toApplication(entry) : toInstallation(entry);
504
594
  }
505
595
  /**
506
596
  * Returns a dashboard application by id, or `null` when it is unavailable.
@@ -622,132 +712,184 @@ function useAuthToken() {
622
712
  return useTopic("auth.token");
623
713
  }
624
714
  /**
625
- * Returns the signed-in user, or `null` while signed out.
715
+ * Returns the capabilities the host provides.
626
716
  *
627
- * Suspends until Dashboard publishes the current user.
717
+ * A capability is something the host provides; an application reads this record to hide its own
718
+ * implementation of anything the host provides, or to skip publishing to a capability the host
719
+ * does not have. A missing key means the host does not provide that capability.
720
+ *
721
+ * Suspends until the host publishes the record.
628
722
  *
629
723
  * @example
630
724
  * ```tsx
631
- * function CurrentUser() {
632
- * const user = useCurrentUser()
633
- * return <span>{user?.name ?? 'Signed out'}</span>
725
+ * function UserMenu() {
726
+ * const {globalUserMenu} = useCapabilities()
727
+ * if (globalUserMenu) return null
728
+ * return <LocalUserMenu />
634
729
  * }
635
730
  * ```
636
731
  *
637
732
  * @public
638
733
  */
639
- function useCurrentUser() {
640
- return useTopic("users.current");
734
+ function useCapabilities() {
735
+ return useTopic("applications.capabilities");
641
736
  }
642
737
  /**
643
- * Returns a stable function that emits a dashboard event topic.
644
- *
645
- * The event is sent immediately. Ignore the lazy result for fire-and-forget delivery, await it
646
- * inside `useTransition` to track a reply, or read it with React `use` to suspend. A reply
647
- * that fails (`NO_RESPONDER`, `TIMEOUT`, `ABORTED`) rejects the result; when read with `use`
648
- * that reaches the nearest error boundary, so pair the `Suspense` with one.
738
+ * Returns the signed-in user, or `null` while signed out.
649
739
  *
650
- * @example Fire and forget
651
- * ```tsx
652
- * function ExpandPanel() {
653
- * const setPanelMode = useEmit('panels.mode.set')
654
- * return (
655
- * <button onClick={() => setPanelMode({name: 'favorites', mode: 'full'})}>
656
- * Expand
657
- * </button>
658
- * )
659
- * }
660
- * ```
740
+ * Suspends until Dashboard publishes the current user.
661
741
  *
662
- * @example Await a reply with Suspense
742
+ * @example
663
743
  * ```tsx
664
- * function Session({request}: {request: MessageBusEmitResult<string>}) {
665
- * use(request)
666
- * return <p>Ready</p>
667
- * }
668
- *
669
- * function SessionAccordion() {
670
- * const refreshToken = useEmit('auth.token.refresh')
671
- * const [request, setRequest] = useState<MessageBusEmitResult<string> | null>(null)
672
- *
673
- * return (
674
- * <details
675
- * onToggle={(event) => setRequest(event.currentTarget.open ? refreshToken() : null)}
676
- * >
677
- * <summary>Session</summary>
678
- * <ErrorBoundary fallback={<p>Could not refresh</p>}>
679
- * <Suspense fallback={<p>Refreshing...</p>}>
680
- * {request && <Session request={request} />}
681
- * </Suspense>
682
- * </ErrorBoundary>
683
- * </details>
684
- * )
744
+ * function CurrentUser() {
745
+ * const user = useCurrentUser()
746
+ * return <span>{user?.name ?? 'Signed out'}</span>
685
747
  * }
686
748
  * ```
687
749
  *
688
750
  * @public
689
751
  */
690
- function useEmit(topic) {
691
- let $ = c(6), instance = useSanityInstance(), t0 = `emit topic "${topic}"`, t1;
692
- $[0] !== instance || $[1] !== t0 ? (t1 = requireDashboardMessageBus(instance, t0), $[0] = instance, $[1] = t0, $[2] = t1) : t1 = $[2];
693
- let messageBus = t1, t2;
694
- return $[3] !== messageBus || $[4] !== topic ? (t2 = (...t3) => {
695
- let args = t3;
696
- return messageBus.emit(topic, ...args);
697
- }, $[3] = messageBus, $[4] = topic, $[5] = t2) : t2 = $[5], t2;
752
+ function useCurrentUser() {
753
+ return useTopic("users.current");
698
754
  }
699
755
  /**
700
756
  * @public
701
757
  *
702
758
  * A helper hook designed to be injected into routing components for apps within the Dashboard.
703
- * While the Dashboard can usually handle navigation, there are special cases when you
704
- * are already within a target app, and need to navigate to another route inside of that app.
759
+ * It keeps the app's router and the Dashboard in two-way sync: `navigateFn` receives the
760
+ * navigations the Dashboard asks the app to perform, and the returned function reports the app's
761
+ * own in-app navigations back to the Dashboard so the browser URL and the Dashboard's router
762
+ * follow along.
705
763
  *
706
- * For example, your user might "favorite" a document inside of your application.
707
- * If they click on the Dashboard favorites item in the sidebar, and are already within your application,
708
- * there needs to be some way for the dashboard to signal to your application to reroute to where that document was favorited.
764
+ * A navigation the app reports is not echoed back through `navigateFn`. The returned function is
765
+ * referentially stable.
709
766
  *
710
- * This hook is intended to receive those messages, and takes a function to route to the correct path.
767
+ * The two Dashboard runtimes differ:
768
+ * - In an iframe (Comlink), inbound `type` may be `'push'`, `'replace'` or `'pop'`. Reporting is
769
+ * optional because the bridge already forwards the iframe's own `pushState`; a reported `type`
770
+ * is ignored and the host applies the URL as a `replace`.
771
+ * - In a federated app (message bus), inbound `type` is `'push'` or `'replace'`; `'pop'` is never
772
+ * sent. Reporting is required because the app's router does not reach the host, and `type` is
773
+ * honoured. Until the Dashboard publishes the app's base path, reports are dropped with a
774
+ * console warning.
711
775
  *
712
776
  * @param navigateFn - Function to handle navigation; should accept:
713
777
  * - `path`: a string, which will be a relative path (for example, 'my-route')
714
778
  * - `type`: 'push', 'replace', or 'pop', which will be the type of navigation to perform
779
+ * @returns A function the app calls to report its own in-app navigations to the Dashboard.
715
780
  *
716
781
  * @example
717
782
  * ```tsx
718
783
  * import {useNavigate} from '@sanity/sdk-react/dashboard'
719
- * import {BrowserRouter, useNavigate as useRouterNavigate} from 'react-router'
720
- * import {Suspense} from 'react'
784
+ * import {useEffect} from 'react'
785
+ * import {useLocation, useNavigate as useRouterNavigate} from 'react-router'
721
786
  *
722
- * function DashboardNavigationHandler() {
723
- * const navigate = useRouterNavigate()
724
- * useNavigate(({path, type}) => {
725
- * navigate(path, {replace: type === 'replace'})
787
+ * function DashboardNavigationSync() {
788
+ * const routerNavigate = useRouterNavigate()
789
+ * const {pathname, search, hash} = useLocation()
790
+ * const navigate = useNavigate(({path, type}) => {
791
+ * routerNavigate(path, {replace: type === 'replace'})
726
792
  * })
793
+ * useEffect(() => {
794
+ * navigate({path: `${pathname.slice(1)}${search}${hash}`})
795
+ * }, [navigate, pathname, search, hash])
727
796
  * return null
728
797
  * }
729
- *
730
- * // Wrap the component with Suspense since the hook may suspend
731
- * function MyApp() {
732
- * return (
733
- * <BrowserRouter>
734
- * <Suspense>
735
- * <DashboardNavigationHandler />
736
- * </Suspense>
737
- * </BrowserRouter>
738
- * )
739
- * }
740
798
  * ```
741
799
  */
742
800
  function useNavigate(navigateFn) {
743
- let $ = c(2), t0;
801
+ return isDashboardEnvironment() ? useBusNavigate(navigateFn) : useComlinkNavigate(navigateFn);
802
+ }
803
+ function useComlinkNavigate(navigateFn) {
804
+ let $ = c(4), t0;
744
805
  $[0] === navigateFn ? t0 = $[1] : (t0 = {
745
806
  name: SDK_NODE_NAME,
746
807
  connectTo: SDK_CHANNEL_NAME,
747
808
  onMessage: { "dashboard/v1/history/change-path": (data) => {
748
809
  navigateFn(data);
749
810
  } }
750
- }, $[0] = navigateFn, $[1] = t0), useWindowConnection(t0);
811
+ }, $[0] = navigateFn, $[1] = t0);
812
+ let { sendMessage } = useWindowConnection(t0), t1;
813
+ return $[2] === sendMessage ? t1 = $[3] : (t1 = (t2) => {
814
+ let { path } = t2;
815
+ return sendMessage("dashboard/v1/bridge/listeners/history/update-url", { url: new URL(path, window.location.origin).href });
816
+ }, $[2] = sendMessage, $[3] = t1), t1;
817
+ }
818
+ function joinPath(base, path) {
819
+ let root = base.replace(/\/$/, "");
820
+ return path ? `${root}/${path}` : root;
821
+ }
822
+ function useBusNavigate(navigateFn) {
823
+ let $ = c(9), instance = useSanityInstance(), t0;
824
+ $[0] === instance ? t0 = $[1] : (t0 = requireDashboardMessageBus(instance, "navigate"), $[0] = instance, $[1] = t0);
825
+ let bus = t0, navigate = useEffectEvent(navigateFn), ownRequest = useRef(null), t1;
826
+ $[2] !== bus || $[3] !== navigate ? (t1 = () => {
827
+ let sub = bus.subscribe("navigation.location").pipe(pairwise(), filter((pair) => {
828
+ let [prev, curr] = pair;
829
+ return curr?.transition === null && curr.appId === bus.appId && prev?.transition?.to.appId === bus.appId && prev.transition.to.path === curr.path;
830
+ }), filter((t2) => {
831
+ let [, curr_0] = t2, suppress = curr_0.path === ownRequest.current;
832
+ return ownRequest.current = null, !suppress;
833
+ }), map(_temp$1)).subscribe((change) => navigate(change));
834
+ return () => sub.unsubscribe();
835
+ }, $[2] = bus, $[3] = navigate, $[4] = t1) : t1 = $[4];
836
+ let t2;
837
+ $[5] === bus ? t2 = $[6] : (t2 = [bus], $[5] = bus, $[6] = t2), useEffect(t1, t2);
838
+ let t3;
839
+ return $[7] === bus ? t3 = $[8] : (t3 = (t4) => {
840
+ let { path, type: t5 } = t4, type = t5 === void 0 ? "push" : t5, own = path.replace(/^\//, "");
841
+ ownRequest.current = own;
842
+ let clearIfStale = () => {
843
+ ownRequest.current === own && (ownRequest.current = null);
844
+ };
845
+ bus.query("applications.base-path").then((base) => {
846
+ if (!base.ok) throw new TopicError$1("applications.base-path");
847
+ return bus.emit("navigation.location.update", {
848
+ url: joinPath(base.value, own),
849
+ history: type
850
+ });
851
+ }).then((reply) => {
852
+ reply.ok || clearIfStale();
853
+ }, (error) => {
854
+ console.warn("Failed to report navigation to the Dashboard", error), clearIfStale();
855
+ });
856
+ }, $[7] = bus, $[8] = t3), t3;
857
+ }
858
+ function _temp$1(t0) {
859
+ let [prev_0, curr_1] = t0;
860
+ return {
861
+ path: curr_1.path,
862
+ type: prev_0.transition.navigationType
863
+ };
864
+ }
865
+ function useComlinkStudioNavigator(documentHandle) {
866
+ let $ = c(5), t0;
867
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = {
868
+ name: SDK_NODE_NAME,
869
+ connectTo: SDK_CHANNEL_NAME
870
+ }, $[0] = t0) : t0 = $[0];
871
+ let { sendMessage } = useWindowConnection(t0), t1;
872
+ return $[1] !== documentHandle.documentId || $[2] !== documentHandle.documentType || $[3] !== sendMessage ? (t1 = (workspace) => {
873
+ let message = {
874
+ type: "dashboard/v1/bridge/navigate-to-resource",
875
+ data: {
876
+ resourceId: workspace.id,
877
+ resourceType: "studio",
878
+ path: `/intent/edit/id=${documentHandle.documentId};type=${documentHandle.documentType}`
879
+ }
880
+ };
881
+ sendMessage(message.type, message.data);
882
+ }, $[1] = documentHandle.documentId, $[2] = documentHandle.documentType, $[3] = sendMessage, $[4] = t1) : t1 = $[4], t1;
883
+ }
884
+ function useBusStudioNavigator(documentHandle) {
885
+ let $ = c(4), navigate = useEmit("navigation.location.update"), t0;
886
+ return $[0] !== documentHandle.documentId || $[1] !== documentHandle.documentType || $[2] !== navigate ? (t0 = (workspace) => {
887
+ let url = urlFor.studios(workspace.userApplicationId).workspace(workspace.name).intent("edit", {
888
+ id: documentHandle.documentId,
889
+ type: documentHandle.documentType
890
+ }).url();
891
+ navigate({ url }).then(_temp, _temp2);
892
+ }, $[0] = documentHandle.documentId, $[1] = documentHandle.documentType, $[2] = navigate, $[3] = t0) : t0 = $[3], t0;
751
893
  }
752
894
  /**
753
895
  * @public
@@ -757,6 +899,9 @@ function useNavigate(navigateFn) {
757
899
  * Uses the `projectId` and `dataset` properties of the {@link DocumentHandle} you provide to resolve the correct Studio.
758
900
  * This will only work if you have deployed a studio with a workspace with this `projectId` / `dataset` combination.
759
901
  *
902
+ * Works in both Dashboard runtimes: it sends the navigation over the Comlink connection in the
903
+ * iframe runtime and over the message bus in the federated runtime.
904
+ *
760
905
  * @remarks If you write your own Document Handle to pass to this hook (as opposed to a Document Handle generated by another hook),
761
906
  * it must include values for `documentId`, `documentType`, `projectId`, and `dataset`.
762
907
  *
@@ -794,41 +939,38 @@ function useNavigate(navigateFn) {
794
939
  * }
795
940
  * ```
796
941
  */
942
+ function _temp2(error) {
943
+ console.warn("Failed to navigate to studio document", error);
944
+ }
945
+ function _temp(reply) {
946
+ reply.ok === !1 && console.warn("Failed to navigate to studio document", reply);
947
+ }
797
948
  function useNavigateToStudioDocument(documentHandle, preferredStudioUrl) {
798
- let $ = c(8), { workspacesByProjectIdAndDataset } = useStudioWorkspacesByProjectIdDataset(), t0;
799
- $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = {
800
- name: SDK_NODE_NAME,
801
- connectTo: SDK_CHANNEL_NAME
802
- }, $[0] = t0) : t0 = $[0];
803
- let { sendMessage } = useWindowConnection(t0), t1;
804
- $[1] !== documentHandle || $[2] !== preferredStudioUrl || $[3] !== sendMessage || $[4] !== workspacesByProjectIdAndDataset ? (t1 = () => {
949
+ let { workspacesByProjectIdAndDataset } = useStudioWorkspacesByProjectIdDataset(), sendToWorkspace = isDashboardEnvironment() ? useBusStudioNavigator(documentHandle) : useComlinkStudioNavigator(documentHandle);
950
+ return { navigateToStudioDocument: useCallback(() => {
805
951
  let { projectId, dataset } = documentHandle;
806
952
  if (!projectId || !dataset) {
807
953
  console.warn("Project ID and dataset are required to navigate to a studio document");
808
954
  return;
809
955
  }
810
- let workspace;
811
- if (preferredStudioUrl) workspace = [...workspacesByProjectIdAndDataset[`${projectId}:${dataset}`] || [], ...workspacesByProjectIdAndDataset["NO_PROJECT_ID:NO_DATASET"] || []].find((w) => w.url === preferredStudioUrl);
812
- else {
813
- let workspaces = workspacesByProjectIdAndDataset[`${projectId}:${dataset}`];
814
- workspaces?.length > 1 && (console.warn("Multiple workspaces found for document and no preferred studio url", documentHandle), console.warn("Using the first one", workspaces[0])), workspace = workspaces?.[0];
815
- }
956
+ let workspace = resolveWorkspace(documentHandle, workspacesByProjectIdAndDataset, preferredStudioUrl);
816
957
  if (!workspace) {
817
958
  console.warn(`No workspace found for document with projectId: ${projectId} and dataset: ${dataset}${preferredStudioUrl ? ` or with preferred studio url: ${preferredStudioUrl}` : ""}`);
818
959
  return;
819
960
  }
820
- let message = {
821
- type: "dashboard/v1/bridge/navigate-to-resource",
822
- data: {
823
- resourceId: workspace.id,
824
- resourceType: "studio",
825
- path: `/intent/edit/id=${documentHandle.documentId};type=${documentHandle.documentType}`
826
- }
827
- };
828
- sendMessage(message.type, message.data);
829
- }, $[1] = documentHandle, $[2] = preferredStudioUrl, $[3] = sendMessage, $[4] = workspacesByProjectIdAndDataset, $[5] = t1) : t1 = $[5];
830
- let navigateToStudioDocument = t1, t2;
831
- return $[6] === navigateToStudioDocument ? t2 = $[7] : (t2 = { navigateToStudioDocument }, $[6] = navigateToStudioDocument, $[7] = t2), t2;
961
+ sendToWorkspace(workspace);
962
+ }, [
963
+ documentHandle,
964
+ workspacesByProjectIdAndDataset,
965
+ preferredStudioUrl,
966
+ sendToWorkspace
967
+ ]) };
968
+ }
969
+ function resolveWorkspace(documentHandle, workspacesByProjectIdAndDataset, preferredStudioUrl) {
970
+ let { projectId, dataset } = documentHandle, key = `${projectId}:${dataset}`;
971
+ if (preferredStudioUrl) return [...workspacesByProjectIdAndDataset[key] || [], ...workspacesByProjectIdAndDataset["NO_PROJECT_ID:NO_DATASET"] || []].find((w) => w.url === preferredStudioUrl);
972
+ let workspaces = workspacesByProjectIdAndDataset[key];
973
+ return workspaces && workspaces.length > 1 && (console.warn("Multiple workspaces found for document and no preferred studio url", documentHandle), console.warn("Using the first one", workspaces[0])), workspaces?.[0];
832
974
  }
833
975
  const remoteClientStates = /* @__PURE__ */ new WeakMap();
834
976
  /** Returns the instance's remote client state, creating it on first use. @internal */
@@ -925,6 +1067,6 @@ function useComlinkWindowTitle(viewTitle) {
925
1067
  };
926
1068
  }, [viewTitle, appTitle]);
927
1069
  }
928
- export { MessageBusError, DashboardTokenRefreshProvider as TokenRefreshProvider, TopicError, UrlBuilder, connectMessageBus, createRemoteInstance, urlFor, useAgentResourceContext, useApplication, useApplicationBasePath, useApplicationConfig, useApplicationConfigs, useApplicationForegroundId, useApplications, useAuthToken, useCurrentUser, useEmit, useNavigate, useNavigateToStudioDocument, useOrganizationId, useRemoteClient, useTopic, useWindowTitle };
1070
+ export { MessageBusError, DashboardTokenRefreshProvider as TokenRefreshProvider, TopicError, UrlBuilder, capabilities, connectMessageBus, createRemoteInstance, urlFor, useAgentResourceContext, useApplication, useApplicationBasePath, useApplicationConfig, useApplicationConfigs, useApplicationContext, useApplicationForegroundId, useApplications, useAuthToken, useCapabilities, useCurrentUser, useEmit, useNavigate, useNavigateToStudioDocument, useOrganizationId, useRemoteClient, useTopic, useWindowTitle };
929
1071
 
930
1072
  //# sourceMappingURL=dashboard.js.map