@sanity/sdk-react 3.1.0 → 3.2.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.
- package/dist/_exports/dashboard-internal.d.ts +2 -0
- package/dist/_exports/dashboard-internal.js +2 -0
- package/dist/_exports/dashboard.d.ts +457 -7
- package/dist/_exports/dashboard.d.ts.map +1 -1
- package/dist/_exports/dashboard.js +623 -2
- package/dist/_exports/dashboard.js.map +1 -1
- package/dist/index.d.ts +90 -87
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/{useStudioWorkspacesByProjectIdDataset-DxUlukmF.js → useStudioWorkspacesByProjectIdDataset-BZ_Ud887.js} +53 -56
- package/dist/useStudioWorkspacesByProjectIdDataset-BZ_Ud887.js.map +1 -0
- package/package.json +34 -32
- package/src/_exports/dashboard-internal.ts +1 -0
- package/src/_exports/dashboard.test-d.ts +64 -0
- package/src/_exports/dashboard.ts +70 -0
- package/src/_exports/index.ts +1 -1
- package/src/components/auth/AuthBoundary.test.tsx +34 -0
- package/src/components/auth/AuthBoundary.tsx +1 -2
- package/src/context/DashboardTokenRefresh.test.tsx +124 -22
- package/src/context/DashboardTokenRefresh.tsx +31 -15
- package/src/dashboard/createRemoteInstance.test.ts +168 -0
- package/src/dashboard/createRemoteInstance.ts +123 -0
- package/src/dashboard/module.ts +39 -0
- package/src/dashboard/remoteClientState.ts +27 -0
- package/src/dashboard/urlFor.test.ts +246 -0
- package/src/dashboard/urlFor.ts +462 -0
- package/src/hooks/applications/useApplication.test-d.ts +1 -1
- package/src/hooks/dashboard/useApplication.test.tsx +59 -0
- package/src/hooks/dashboard/useApplication.ts +22 -0
- package/src/hooks/dashboard/useApplicationConfig.test.tsx +106 -0
- package/src/hooks/dashboard/useApplicationConfig.ts +45 -0
- package/src/hooks/dashboard/useApplicationConfigs.test.tsx +89 -0
- package/src/hooks/dashboard/useApplicationConfigs.ts +26 -0
- package/src/hooks/dashboard/useApplicationForegroundId.test.tsx +54 -0
- package/src/hooks/dashboard/useApplicationForegroundId.ts +22 -0
- package/src/hooks/dashboard/useApplications.test.tsx +249 -0
- package/src/hooks/dashboard/useApplications.ts +128 -0
- package/src/hooks/dashboard/useEmit.test.tsx +124 -0
- package/src/hooks/dashboard/useEmit.ts +78 -0
- package/src/hooks/dashboard/useNavigate.ts +1 -3
- package/src/hooks/dashboard/useRemoteClient.test.tsx +88 -0
- package/src/hooks/dashboard/useRemoteClient.ts +10 -0
- package/src/hooks/dashboard/useTopic.test.tsx +200 -0
- package/src/hooks/dashboard/useTopic.ts +29 -0
- package/src/hooks/dashboard/useUpdateFavorite.test.tsx +8 -1
- package/src/hooks/document/useApplyDocumentActions.ts +3 -4
- package/src/hooks/document/useCreateDocument.ts +2 -3
- package/src/hooks/document/useDocument.ts +11 -6
- package/src/hooks/document/useEditDocument.ts +5 -5
- package/src/hooks/projection/useDocumentProjection.ts +6 -7
- package/src/hooks/query/useQuery.ts +3 -4
- package/dist/useStudioWorkspacesByProjectIdDataset-DxUlukmF.js.map +0 -1
- package/src/context/dashboardToken.ts +0 -63
|
@@ -1,9 +1,385 @@
|
|
|
1
|
-
import {
|
|
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
|