@sanity/sdk-react 3.1.0 → 3.3.0-rc.0

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 (63) hide show
  1. package/dist/_exports/dashboard-internal.d.ts +2 -0
  2. package/dist/_exports/dashboard-internal.js +2 -0
  3. package/dist/_exports/dashboard.d.ts +457 -7
  4. package/dist/_exports/dashboard.d.ts.map +1 -1
  5. package/dist/_exports/dashboard.js +623 -2
  6. package/dist/_exports/dashboard.js.map +1 -1
  7. package/dist/index.d.ts +227 -87
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +147 -30
  10. package/dist/index.js.map +1 -1
  11. package/dist/{useStudioWorkspacesByProjectIdDataset-DxUlukmF.js → useStudioWorkspacesByProjectIdDataset-BZ_Ud887.js} +53 -56
  12. package/dist/useStudioWorkspacesByProjectIdDataset-BZ_Ud887.js.map +1 -0
  13. package/package.json +34 -32
  14. package/src/_exports/dashboard-internal.ts +1 -0
  15. package/src/_exports/dashboard.test-d.ts +64 -0
  16. package/src/_exports/dashboard.ts +70 -0
  17. package/src/_exports/index.ts +1 -1
  18. package/src/_exports/sdk-react.ts +3 -0
  19. package/src/components/auth/AuthBoundary.test.tsx +115 -2
  20. package/src/components/auth/AuthBoundary.tsx +18 -5
  21. package/src/components/auth/LoginCallback.test.tsx +46 -7
  22. package/src/components/auth/LoginCallback.tsx +22 -4
  23. package/src/context/DashboardTokenRefresh.test.tsx +124 -22
  24. package/src/context/DashboardTokenRefresh.tsx +31 -15
  25. package/src/dashboard/createRemoteInstance.test.ts +168 -0
  26. package/src/dashboard/createRemoteInstance.ts +123 -0
  27. package/src/dashboard/module.ts +39 -0
  28. package/src/dashboard/remoteClientState.ts +27 -0
  29. package/src/dashboard/urlFor.test.ts +246 -0
  30. package/src/dashboard/urlFor.ts +462 -0
  31. package/src/hooks/applications/useApplication.test-d.ts +1 -1
  32. package/src/hooks/auth/useHandleOAuthCallback.test.tsx +16 -0
  33. package/src/hooks/auth/useHandleOAuthCallback.tsx +49 -0
  34. package/src/hooks/auth/useOAuthAuthorize.test.tsx +16 -0
  35. package/src/hooks/auth/useOAuthAuthorize.tsx +28 -0
  36. package/src/hooks/auth/useOAuthTokens.test.tsx +240 -0
  37. package/src/hooks/auth/useOAuthTokens.tsx +95 -0
  38. package/src/hooks/dashboard/useApplication.test.tsx +59 -0
  39. package/src/hooks/dashboard/useApplication.ts +22 -0
  40. package/src/hooks/dashboard/useApplicationConfig.test.tsx +106 -0
  41. package/src/hooks/dashboard/useApplicationConfig.ts +45 -0
  42. package/src/hooks/dashboard/useApplicationConfigs.test.tsx +89 -0
  43. package/src/hooks/dashboard/useApplicationConfigs.ts +26 -0
  44. package/src/hooks/dashboard/useApplicationForegroundId.test.tsx +54 -0
  45. package/src/hooks/dashboard/useApplicationForegroundId.ts +22 -0
  46. package/src/hooks/dashboard/useApplications.test.tsx +249 -0
  47. package/src/hooks/dashboard/useApplications.ts +128 -0
  48. package/src/hooks/dashboard/useEmit.test.tsx +124 -0
  49. package/src/hooks/dashboard/useEmit.ts +78 -0
  50. package/src/hooks/dashboard/useNavigate.ts +1 -3
  51. package/src/hooks/dashboard/useRemoteClient.test.tsx +88 -0
  52. package/src/hooks/dashboard/useRemoteClient.ts +10 -0
  53. package/src/hooks/dashboard/useTopic.test.tsx +200 -0
  54. package/src/hooks/dashboard/useTopic.ts +29 -0
  55. package/src/hooks/dashboard/useUpdateFavorite.test.tsx +8 -1
  56. package/src/hooks/document/useApplyDocumentActions.ts +3 -4
  57. package/src/hooks/document/useCreateDocument.ts +2 -3
  58. package/src/hooks/document/useDocument.ts +11 -6
  59. package/src/hooks/document/useEditDocument.ts +5 -5
  60. package/src/hooks/projection/useDocumentProjection.ts +6 -7
  61. package/src/hooks/query/useQuery.ts +3 -4
  62. package/dist/useStudioWorkspacesByProjectIdDataset-DxUlukmF.js.map +0 -1
  63. package/src/context/dashboardToken.ts +0 -63
@@ -1,9 +1,385 @@
1
- import { a as useWindowConnection, n as useOrganizationId, r as DashboardTokenRefreshProvider, t as useStudioWorkspacesByProjectIdDataset } from "../useStudioWorkspacesByProjectIdDataset-DxUlukmF.js";
1
+ import { i as useWindowConnection, n as useOrganizationId, o as createStateSourceHook, r as DashboardTokenRefreshProvider, s as useSanityInstance, t as useStudioWorkspacesByProjectIdDataset } from "../useStudioWorkspacesByProjectIdDataset-BZ_Ud887.js";
2
2
  import { c } from "react-compiler-runtime";
3
3
  import "@sanity/sdk";
4
+ import { createLogger, getTopicState, requireDashboardMessageBus, resolveTopic } from "@sanity/sdk/_internal";
4
5
  import { useEffect, useRef, useState } from "react";
5
6
  import { SDK_CHANNEL_NAME, SDK_NODE_NAME } from "@sanity/message-protocol";
6
7
  import "@sanity/sdk/comlink";
8
+ import { of } from "rxjs";
9
+ import { MessageBusError, TopicError, connectMessageBus } from "@sanity/sdk/dashboard";
10
+ import { createInstance } from "@module-federation/runtime";
11
+ import "@module-federation/runtime/types";
12
+ const MANIFEST_FILE = "mf-manifest.json";
13
+ function withManifest(remote) {
14
+ if (remote.entry.endsWith(`/${MANIFEST_FILE}`)) return remote;
15
+ let base = remote.entry.endsWith("/") ? remote.entry : `${remote.entry}/`;
16
+ return {
17
+ ...remote,
18
+ entry: new URL(MANIFEST_FILE, base).href
19
+ };
20
+ }
21
+ /**
22
+ * Creates a client for registering, loading, and preloading federated modules.
23
+ * @public
24
+ */
25
+ function createRemoteInstance(options) {
26
+ let logger = createLogger(options.name), instance = createInstance({
27
+ ...options,
28
+ plugins: [log(logger.debug), ...options.plugins ?? []],
29
+ remotes: options.remotes?.map((remote) => "entry" in remote ? withManifest(remote) : remote) ?? []
30
+ });
31
+ return {
32
+ registerRemotes: (remotes) => instance.registerRemotes(remotes.map(withManifest), { force: !1 }),
33
+ loadRemote: async (id) => {
34
+ let remoteModule;
35
+ try {
36
+ remoteModule = await instance.loadRemote(id);
37
+ } catch (error) {
38
+ throw Error(`Failed to load remote module "${id}"`, { cause: error });
39
+ }
40
+ return remoteModule;
41
+ },
42
+ preloadRemote: (name, exposes) => instance.preloadRemote([{
43
+ nameOrAlias: name,
44
+ exposes,
45
+ resourceCategory: "all"
46
+ }])
47
+ };
48
+ }
49
+ function log(onDebug) {
50
+ let logEvent = (eventName, args) => {
51
+ let { origin: _origin, ...data } = args;
52
+ onDebug(`[Lifecycle] ${eventName}`, {
53
+ ...data,
54
+ internal: !0
55
+ });
56
+ };
57
+ return {
58
+ name: "sanity-logger",
59
+ beforeInit(args) {
60
+ return logEvent("beforeInit", args), args;
61
+ },
62
+ beforeRegisterRemote(args) {
63
+ return logEvent("beforeRegisterRemote", args), args;
64
+ },
65
+ beforePreloadRemote(args) {
66
+ logEvent("beforePreloadRemote", args);
67
+ },
68
+ beforeRequest(args) {
69
+ return logEvent("beforeRequest", args), args;
70
+ },
71
+ afterResolve(args) {
72
+ return logEvent("afterResolve", args), args;
73
+ },
74
+ onLoad(args) {
75
+ return logEvent("onLoad", args), args;
76
+ },
77
+ loadShare(args) {
78
+ logEvent("loadShare", args);
79
+ },
80
+ beforeLoadShare(args) {
81
+ return logEvent("beforeLoadShare", args), args;
82
+ }
83
+ };
84
+ }
85
+ function _checkPrivateRedeclaration(e, t) {
86
+ if (t.has(e)) throw TypeError("Cannot initialize the same private elements twice on an object");
87
+ }
88
+ function _classPrivateMethodInitSpec(e, a) {
89
+ _checkPrivateRedeclaration(e, a), a.add(e);
90
+ }
91
+ function _classPrivateFieldInitSpec(e, t, a) {
92
+ _checkPrivateRedeclaration(e, t), t.set(e, a);
93
+ }
94
+ function _assertClassBrand(e, t, n) {
95
+ if (typeof e == "function" ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
96
+ throw TypeError("Private element is not present on this object");
97
+ }
98
+ function _classPrivateFieldSet2(s, a, r) {
99
+ return s.set(_assertClassBrand(s, a), r), r;
100
+ }
101
+ function _classPrivateFieldGet2(s, a) {
102
+ return s.get(_assertClassBrand(s, a));
103
+ }
104
+ function _typeof(o) {
105
+ "@babel/helpers - typeof";
106
+ return _typeof = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(o) {
107
+ return typeof o;
108
+ } : function(o) {
109
+ return o && typeof Symbol == "function" && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
110
+ }, _typeof(o);
111
+ }
112
+ function toPrimitive(t, r) {
113
+ if (_typeof(t) != "object" || !t) return t;
114
+ var e = t[Symbol.toPrimitive];
115
+ if (e !== void 0) {
116
+ var i = e.call(t, r || "default");
117
+ if (_typeof(i) != "object") return i;
118
+ throw TypeError("@@toPrimitive must return a primitive value.");
119
+ }
120
+ return (r === "string" ? String : Number)(t);
121
+ }
122
+ function toPropertyKey(t) {
123
+ var i = toPrimitive(t, "string");
124
+ return _typeof(i) == "symbol" ? i : i + "";
125
+ }
126
+ function _defineProperty(e, r, t) {
127
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
128
+ value: t,
129
+ enumerable: !0,
130
+ configurable: !0,
131
+ writable: !0
132
+ }) : e[r] = t, e;
133
+ }
134
+ const relativeUrlBase = "https://dashboard.invalid", splitPath = (path) => path.flatMap((part) => part.split("/")).filter(Boolean), intentParametersOf = (...[intent, parameters]) => {
135
+ let searchParameters = new URLSearchParams();
136
+ switch (intent) {
137
+ case "edit": {
138
+ let { id, type, mode } = parameters;
139
+ searchParameters.set("id", id), type !== void 0 && searchParameters.set("type", type), mode !== void 0 && searchParameters.set("mode", mode);
140
+ break;
141
+ }
142
+ case "create":
143
+ searchParameters.set("template", parameters.template), searchParameters.set("type", parameters.type);
144
+ break;
145
+ case "release": searchParameters.set("id", parameters.id);
146
+ }
147
+ return searchParameters;
148
+ }, parseIntentParameters = (parameters) => new URLSearchParams(parameters.replaceAll(";", "&")), serializeIntentParameters = (parameters) => Array.from(parameters, ([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`).join(";"), appendSegments = (url, segments) => {
149
+ let nextUrl = new URL(url);
150
+ return segments.length === 0 || (nextUrl.pathname = `${nextUrl.pathname.replace(/\/$/, "")}/${segments.map(encodeURIComponent).join("/")}`), nextUrl;
151
+ };
152
+ var _url = /* @__PURE__ */ new WeakMap(), _UrlBuilder_brand = /* @__PURE__ */ new WeakSet(), UrlBuilder = class {
153
+ /**
154
+ * Creates an immutable URL builder at the supplied URL.
155
+ *
156
+ * @example
157
+ * ```ts
158
+ * const builder = new UrlBuilder(
159
+ * new URL('/applications/my-app', 'https://dashboard.sanity.io'),
160
+ * )
161
+ *
162
+ * builder.url() // '/applications/my-app'
163
+ * ```
164
+ */
165
+ constructor(url) {
166
+ _classPrivateMethodInitSpec(this, _UrlBuilder_brand), _classPrivateFieldInitSpec(this, _url, void 0), _classPrivateFieldSet2(_url, this, new URL(url));
167
+ }
168
+ /**
169
+ * Returns a builder with each value appended as one encoded path segment.
170
+ *
171
+ * Route literals and identifiers can be passed together. A slash inside a value stays within
172
+ * that segment instead of creating another route level.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * class DocumentUrlBuilder extends UrlBuilder {
177
+ * document(documentId: string) {
178
+ * return this.append('documents', documentId)
179
+ * }
180
+ * }
181
+ *
182
+ * const builder = new DocumentUrlBuilder(new URL('https://dashboard.sanity.io'))
183
+ * builder.document('drafts/document-1').url()
184
+ * // '/documents/drafts%2Fdocument-1'
185
+ * ```
186
+ */
187
+ append(...segments) {
188
+ return _assertClassBrand(_UrlBuilder_brand, this, _create).call(this, appendSegments(_classPrivateFieldGet2(_url, this), segments));
189
+ }
190
+ /**
191
+ * Returns a builder with changes made through the platform `URL` API.
192
+ *
193
+ * This supports URL details outside the path grammar, including search parameters and hashes.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * class DocumentUrlBuilder extends UrlBuilder {
198
+ * perspective(name: string) {
199
+ * return this.edit((url) => url.searchParams.set('perspective', name))
200
+ * }
201
+ *
202
+ * panel(panelId: string) {
203
+ * return this.edit((url) => {
204
+ * url.hash = `panel/${panelId}`
205
+ * })
206
+ * }
207
+ * }
208
+ * ```
209
+ */
210
+ edit(update) {
211
+ let url = new URL(_classPrivateFieldGet2(_url, this));
212
+ return update(url), _assertClassBrand(_UrlBuilder_brand, this, _create).call(this, url);
213
+ }
214
+ /**
215
+ * Returns another builder type rooted at the appended path segments.
216
+ *
217
+ * Use this inside a custom builder method only when its route enters a child grammar with
218
+ * different methods; use `append` when chaining stays on the current grammar.
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * class DocumentUrlBuilder extends UrlBuilder {
223
+ * perspective(name: string) {
224
+ * return this.edit((url) => url.searchParams.set('perspective', name))
225
+ * }
226
+ * }
227
+ *
228
+ * class ApplicationUrlBuilder extends UrlBuilder {
229
+ * document(documentId: string) {
230
+ * return this.transitionTo(DocumentUrlBuilder, 'documents', documentId)
231
+ * }
232
+ * }
233
+ *
234
+ * const builder = new ApplicationUrlBuilder(new URL('https://dashboard.sanity.io'))
235
+ * builder.document('document-1').perspective('published').url()
236
+ * // '/documents/document-1?perspective=published'
237
+ * ```
238
+ */
239
+ transitionTo(Builder, ...segments) {
240
+ return new Builder(appendSegments(_classPrivateFieldGet2(_url, this), segments));
241
+ }
242
+ /**
243
+ * Returns the URL as a relative string by default.
244
+ *
245
+ * Supplying an origin returns an absolute URL string for the same dashboard route.
246
+ *
247
+ * @example
248
+ * ```ts
249
+ * const builder = new UrlBuilder(
250
+ * new URL('/applications/my-app', 'https://dashboard.sanity.io'),
251
+ * )
252
+ *
253
+ * builder.url() // '/applications/my-app'
254
+ * builder.url({origin: 'https://dashboard.sanity.io'})
255
+ * // 'https://dashboard.sanity.io/applications/my-app'
256
+ * ```
257
+ */
258
+ url(options) {
259
+ let relativeUrl = _assertClassBrand(_UrlBuilder_brand, this, _relativeUrl).call(this);
260
+ return options?.origin === void 0 ? relativeUrl : new URL(relativeUrl, options.origin).href;
261
+ }
262
+ /**
263
+ * Returns the URL as a platform `URL` object using the supplied origin.
264
+ *
265
+ * @example
266
+ * ```ts
267
+ * const builder = new UrlBuilder(
268
+ * new URL('/applications/my-app', 'https://dashboard.sanity.io'),
269
+ * )
270
+ *
271
+ * builder.toURL({origin: 'https://dashboard.sanity.io'}).pathname
272
+ * // '/applications/my-app'
273
+ * ```
274
+ */
275
+ toURL({ origin }) {
276
+ return new URL(_assertClassBrand(_UrlBuilder_brand, this, _relativeUrl).call(this), origin);
277
+ }
278
+ /**
279
+ * Returns the same relative URL as {@link UrlBuilder.url}.
280
+ *
281
+ * @example
282
+ * ```ts
283
+ * const builder = new UrlBuilder(
284
+ * new URL('/applications/my-app', 'https://dashboard.sanity.io'),
285
+ * )
286
+ *
287
+ * `${builder}` // '/applications/my-app'
288
+ * ```
289
+ */
290
+ toString() {
291
+ return this.url();
292
+ }
293
+ };
294
+ function _relativeUrl() {
295
+ return `${_classPrivateFieldGet2(_url, this).pathname}${_classPrivateFieldGet2(_url, this).search}${_classPrivateFieldGet2(_url, this).hash}`;
296
+ }
297
+ function _create(url) {
298
+ let Builder = this.constructor;
299
+ return new Builder(url);
300
+ }
301
+ var CoreApplicationUrlBuilder = class extends UrlBuilder {
302
+ path(...path) {
303
+ return this.append(...splitPath(path));
304
+ }
305
+ };
306
+ _defineProperty(CoreApplicationUrlBuilder, "namespace", "applications");
307
+ var MediaLibraryUrlBuilder = class extends UrlBuilder {
308
+ asset(assetId) {
309
+ return this.append("assets", assetId);
310
+ }
311
+ collection(collectionId) {
312
+ return this.append("collections", collectionId);
313
+ }
314
+ };
315
+ _defineProperty(MediaLibraryUrlBuilder, "namespace", "media");
316
+ var CanvasUrlBuilder = class extends UrlBuilder {
317
+ document(documentId) {
318
+ return this.append("doc", documentId);
319
+ }
320
+ };
321
+ _defineProperty(CanvasUrlBuilder, "namespace", "canvas");
322
+ var StudioUrlBuilder = class extends UrlBuilder {
323
+ workspace(workspace) {
324
+ return this.append(workspace);
325
+ }
326
+ intent(...args) {
327
+ return this.append("intent", args[0]).edit((url) => {
328
+ url.pathname = `${url.pathname}/${serializeIntentParameters(intentParametersOf(...args))}/`;
329
+ });
330
+ }
331
+ perspective(perspective) {
332
+ return this.edit((url) => url.searchParams.set("perspective", perspective));
333
+ }
334
+ comment(commentId) {
335
+ return this.edit((url) => {
336
+ let segments = url.pathname.split("/"), parametersIndex = segments.length - 2, parameters = parseIntentParameters(segments[parametersIndex]);
337
+ parameters.set("inspect", "sanity/comments"), parameters.set("comment", commentId), segments[parametersIndex] = serializeIntentParameters(parameters), url.pathname = segments.join("/");
338
+ });
339
+ }
340
+ task(taskId) {
341
+ return this.edit((url) => url.searchParams.set("selectedTask", taskId));
342
+ }
343
+ path(...path) {
344
+ return this.append(...splitPath(path));
345
+ }
346
+ };
347
+ _defineProperty(StudioUrlBuilder, "namespace", "studios");
348
+ const createRootBuilder = (Builder, ...segments) => new Builder(appendSegments(new URL(relativeUrlBase), [Builder.namespace, ...segments])), createUrls = (builders) => {
349
+ function studios(appId) {
350
+ return appId === void 0 ? new UrlBuilder(new URL("/studios/", relativeUrlBase)) : createRootBuilder(StudioUrlBuilder, appId);
351
+ }
352
+ function applications(appId) {
353
+ return appId === void 0 ? new UrlBuilder(new URL("/applications/", relativeUrlBase)) : createRootBuilder(CoreApplicationUrlBuilder, appId);
354
+ }
355
+ let methods = Object.fromEntries(Object.entries(builders).map(([name, Builder]) => [name, () => createRootBuilder(Builder)])), urls = {
356
+ studios,
357
+ applications,
358
+ mediaLibrary: () => createRootBuilder(MediaLibraryUrlBuilder),
359
+ canvas: () => createRootBuilder(CanvasUrlBuilder),
360
+ home: () => new UrlBuilder(new URL(relativeUrlBase)),
361
+ extend(addedBuilders) {
362
+ let namespaces = /* @__PURE__ */ new Set([
363
+ "",
364
+ StudioUrlBuilder.namespace,
365
+ CoreApplicationUrlBuilder.namespace,
366
+ MediaLibraryUrlBuilder.namespace,
367
+ CanvasUrlBuilder.namespace,
368
+ ...Object.values(builders).map((Builder) => Builder.namespace)
369
+ ]);
370
+ for (let [name, Builder] of Object.entries(addedBuilders)) {
371
+ if (name in urls || name in methods) throw Error(`URL builder "${name}" already exists`);
372
+ if (namespaces.has(Builder.namespace)) throw Error(`URL namespace "${Builder.namespace}" already exists`);
373
+ namespaces.add(Builder.namespace);
374
+ }
375
+ return createUrls({
376
+ ...builders,
377
+ ...addedBuilders
378
+ });
379
+ }
380
+ };
381
+ return Object.assign(urls, methods);
382
+ }, urlFor = createUrls({});
7
383
  /**
8
384
  * @public
9
385
  * Hook for emitting agent resource context updates to the Dashboard.
@@ -70,6 +446,232 @@ function useAgentResourceContext(options) {
70
446
  }, t3 = [updateContext], $[6] = updateContext, $[7] = t2, $[8] = t3), useEffect(t2, t3);
71
447
  }
72
448
  /**
449
+ * Returns the current value of a dashboard state topic and follows later updates.
450
+ *
451
+ * The hook suspends until the topic publishes its first value, using the message bus query
452
+ * deadline. A topic declared with `TopicResult` resolves to its successful value; a
453
+ * failed result throws a `TopicError` to the nearest error boundary.
454
+ *
455
+ * @example
456
+ * ```tsx
457
+ * function ForegroundApplication() {
458
+ * const foregroundId = useTopic('applications.foreground')
459
+ * return <span>{foregroundId ?? 'No application in the foreground'}</span>
460
+ * }
461
+ * ```
462
+ *
463
+ * @public
464
+ */
465
+ const useTopic = createStateSourceHook({
466
+ getState: getTopicState,
467
+ shouldSuspend: (instance, topic) => getTopicState(instance, topic).getCurrent() === void 0,
468
+ suspender: resolveTopic
469
+ }), applicationOrigin = (application) => {
470
+ if (application.externalUrl !== null) return new URL(application.externalUrl).origin;
471
+ if (application.slug === null) return null;
472
+ let staging = globalThis.__SANITY_STAGING__ === !0;
473
+ if (application.isSingleton) {
474
+ let domain = staging ? "run.sanity.work" : "sanity.run";
475
+ return `https://${application.slug}-apps-${application.organizationId}.${domain}`;
476
+ }
477
+ let domain = staging ? "studio.sanity.work" : "sanity.studio";
478
+ return `https://${application.slug}.${domain}`;
479
+ }, loadableInterfaces = ({ activeDeployment, config }) => config?.mfManifest === void 0 ? [] : activeDeployment?.interfaces ?? [], toApplication = (application) => {
480
+ let { activeDeployment: _activeDeployment, config: _config, ...applicationBase } = application, interfaces = loadableInterfaces(application), entry = interfaces.length === 0 ? null : applicationOrigin(applicationBase);
481
+ if (entry === null) return {
482
+ ...applicationBase,
483
+ views: [],
484
+ webWorkers: []
485
+ };
486
+ let views = [], webWorkers = [];
487
+ for (let extension of interfaces) {
488
+ let module = {
489
+ entry,
490
+ moduleId: `${applicationBase.id}/${extension.moduleId}`,
491
+ version: extension.version
492
+ };
493
+ if (extension.type === "worker") {
494
+ webWorkers.push({
495
+ ...extension,
496
+ application: applicationBase,
497
+ module
498
+ });
499
+ continue;
500
+ }
501
+ let { type, ...view } = extension;
502
+ views.push({
503
+ ...view,
504
+ application: applicationBase,
505
+ module,
506
+ surface: type === "app" ? "window" : type
507
+ });
508
+ }
509
+ return {
510
+ ...applicationBase,
511
+ views,
512
+ webWorkers
513
+ };
514
+ };
515
+ /**
516
+ * Returns the applications available in the dashboard.
517
+ *
518
+ * Suspends until the dashboard publishes its application list; a cleared list is empty. Throws a
519
+ * `TopicError` to the nearest error boundary when the dashboard fails to load applications.
520
+ *
521
+ * @example
522
+ * ```tsx
523
+ * function Applications() {
524
+ * const applications = useApplications()
525
+ * return applications.map((application) => <div key={application.id}>{application.title}</div>)
526
+ * }
527
+ * ```
528
+ *
529
+ * @public
530
+ */
531
+ function useApplications() {
532
+ let $ = c(2), applications = useTopic("applications.list"), t0;
533
+ return $[0] === applications ? t0 = $[1] : (t0 = applications?.map(toApplication) ?? [], $[0] = applications, $[1] = t0), t0;
534
+ }
535
+ /**
536
+ * Returns a dashboard application by id, or `null` when it is unavailable.
537
+ *
538
+ * Suspends until the dashboard publishes its application list.
539
+ *
540
+ * @example
541
+ * ```tsx
542
+ * function SelectedApplication({id}: {id: string}) {
543
+ * const application = useApplication(id)
544
+ * return application ? <h1>{application.title}</h1> : <p>Application unavailable</p>
545
+ * }
546
+ * ```
547
+ *
548
+ * @public
549
+ */
550
+ function useApplication(applicationId) {
551
+ return useApplications().find((t0) => {
552
+ let { id } = t0;
553
+ return id === applicationId;
554
+ }) ?? null;
555
+ }
556
+ const NO_CONFIGS = Object.freeze([]);
557
+ /**
558
+ * Returns the application configuration modules available in the dashboard.
559
+ *
560
+ * Suspends until the dashboard publishes its application configs; a cleared list is empty.
561
+ *
562
+ * @example
563
+ * ```tsx
564
+ * function Applications() {
565
+ * const configs = useApplicationConfigs()
566
+ * return configs.map((config) => <div key={config.moduleId}>{config.appType}</div>)
567
+ * }
568
+ * ```
569
+ *
570
+ * @public
571
+ */
572
+ function useApplicationConfigs() {
573
+ return useTopic("applications.config") ?? NO_CONFIGS;
574
+ }
575
+ /**
576
+ * Returns an application configuration by application id or application type, or `null` when
577
+ * none matches.
578
+ *
579
+ * An `appType` query matches the type-level config only (the one without an `appId`), so it is
580
+ * independent of the order the dashboard published configs in. An `appId` query matches that
581
+ * application's config.
582
+ *
583
+ * Suspends until the dashboard publishes its application configs.
584
+ *
585
+ * @example
586
+ * ```tsx
587
+ * function MediaLibraryConfig() {
588
+ * const config = useApplicationConfig({appType: 'media-library'})
589
+ * return config ? <span>{config.moduleId}</span> : <p>Not installed</p>
590
+ * }
591
+ * ```
592
+ *
593
+ * @public
594
+ */
595
+ function useApplicationConfig(selector) {
596
+ let $ = c(3), configs = useApplicationConfigs(), t0;
597
+ return $[0] !== configs || $[1] !== selector ? (t0 = configs.find((config) => selector.appId === void 0 ? config.appType === selector.appType && config.appId === void 0 : config.appId === selector.appId) ?? null, $[0] = configs, $[1] = selector, $[2] = t0) : t0 = $[2], t0;
598
+ }
599
+ /**
600
+ * Returns the id of the application in the foreground, or `null` when Dashboard has none.
601
+ *
602
+ * Suspends until Dashboard publishes the foreground application.
603
+ *
604
+ * @example
605
+ * ```tsx
606
+ * function ForegroundApplication() {
607
+ * const foregroundId = useApplicationForegroundId()
608
+ * return <span>{foregroundId ?? 'No foreground application'}</span>
609
+ * }
610
+ * ```
611
+ *
612
+ * @public
613
+ */
614
+ function useApplicationForegroundId() {
615
+ return useTopic("applications.foreground");
616
+ }
617
+ /**
618
+ * Returns a stable function that emits a dashboard event topic.
619
+ *
620
+ * The event is sent immediately. Ignore the lazy result for fire-and-forget delivery, await it
621
+ * inside `useTransition` to track a reply, or read it with React `use` to suspend. A reply
622
+ * that fails (`NO_RESPONDER`, `TIMEOUT`, `ABORTED`) rejects the result; when read with `use`
623
+ * that reaches the nearest error boundary, so pair the `Suspense` with one.
624
+ *
625
+ * @example Fire and forget
626
+ * ```tsx
627
+ * function ExpandPanel() {
628
+ * const setPanelMode = useEmit('panels.mode.set')
629
+ * return (
630
+ * <button onClick={() => setPanelMode({name: 'favorites', mode: 'full'})}>
631
+ * Expand
632
+ * </button>
633
+ * )
634
+ * }
635
+ * ```
636
+ *
637
+ * @example Await a reply with Suspense
638
+ * ```tsx
639
+ * function Session({request}: {request: MessageBusEmitResult<string>}) {
640
+ * use(request)
641
+ * return <p>Ready</p>
642
+ * }
643
+ *
644
+ * function SessionAccordion() {
645
+ * const refreshToken = useEmit('auth.token.refresh')
646
+ * const [request, setRequest] = useState<MessageBusEmitResult<string> | null>(null)
647
+ *
648
+ * return (
649
+ * <details
650
+ * onToggle={(event) => setRequest(event.currentTarget.open ? refreshToken() : null)}
651
+ * >
652
+ * <summary>Session</summary>
653
+ * <ErrorBoundary fallback={<p>Could not refresh</p>}>
654
+ * <Suspense fallback={<p>Refreshing...</p>}>
655
+ * {request && <Session request={request} />}
656
+ * </Suspense>
657
+ * </ErrorBoundary>
658
+ * </details>
659
+ * )
660
+ * }
661
+ * ```
662
+ *
663
+ * @public
664
+ */
665
+ function useEmit(topic) {
666
+ let $ = c(6), instance = useSanityInstance(), t0 = `emit topic "${topic}"`, t1;
667
+ $[0] !== instance || $[1] !== t0 ? (t1 = requireDashboardMessageBus(instance, t0), $[0] = instance, $[1] = t0, $[2] = t1) : t1 = $[2];
668
+ let messageBus = t1, t2;
669
+ return $[3] !== messageBus || $[4] !== topic ? (t2 = (...t3) => {
670
+ let args = t3;
671
+ return messageBus.emit(topic, ...args);
672
+ }, $[3] = messageBus, $[4] = topic, $[5] = t2) : t2 = $[5], t2;
673
+ }
674
+ /**
73
675
  * @public
74
676
  *
75
677
  * A helper hook designed to be injected into routing components for apps within the Dashboard.
@@ -203,6 +805,25 @@ function useNavigateToStudioDocument(documentHandle, preferredStudioUrl) {
203
805
  let navigateToStudioDocument = t1, t2;
204
806
  return $[6] === navigateToStudioDocument ? t2 = $[7] : (t2 = { navigateToStudioDocument }, $[6] = navigateToStudioDocument, $[7] = t2), t2;
205
807
  }
808
+ const remoteClientStates = /* @__PURE__ */ new WeakMap();
809
+ /** Returns the instance's remote client state, creating it on first use. @internal */
810
+ function getRemoteClientState(instance) {
811
+ if (instance.isDisposed()) throw Error("Cannot create a remote client for a disposed Sanity instance");
812
+ let current = remoteClientStates.get(instance);
813
+ if (current) return current;
814
+ let client = createRemoteInstance({ name: `sanity-remote-${instance.instanceId}` }), state = {
815
+ getCurrent: () => client,
816
+ observable: of(client),
817
+ subscribe: () => () => {}
818
+ };
819
+ return remoteClientStates.set(instance, state), instance.onDispose(() => remoteClientStates.delete(instance)), state;
820
+ }
821
+ /**
822
+ * Returns a remote client shared by hooks using the same Sanity instance.
823
+ * The first call creates the client; later calls reuse it.
824
+ * @public
825
+ */
826
+ const useRemoteClient = createStateSourceHook(getRemoteClientState);
206
827
  function resolveAppTitle(resource) {
207
828
  return resource.manifest?.title || resource.activeDeployment?.manifest?.title || resource.title;
208
829
  }
@@ -273,6 +894,6 @@ function useWindowTitle(viewTitle) {
273
894
  };
274
895
  }, [viewTitle, appTitle]);
275
896
  }
276
- export { DashboardTokenRefreshProvider as TokenRefreshProvider, useAgentResourceContext, useNavigate, useNavigateToStudioDocument, useOrganizationId, useWindowTitle };
897
+ export { MessageBusError, DashboardTokenRefreshProvider as TokenRefreshProvider, TopicError, UrlBuilder, connectMessageBus, createRemoteInstance, urlFor, useAgentResourceContext, useApplication, useApplicationConfig, useApplicationConfigs, useApplicationForegroundId, useApplications, useEmit, useNavigate, useNavigateToStudioDocument, useOrganizationId, useRemoteClient, useTopic, useWindowTitle };
277
898
 
278
899
  //# sourceMappingURL=dashboard.js.map