@sanity/workbench 0.1.0-alpha.6 → 0.1.0-alpha.8
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/{log.js → _chunks-es/index.js} +7 -2
- package/dist/_chunks-es/index.js.map +1 -0
- package/dist/_internal.d.ts +13 -6
- package/dist/_internal.js +20 -9
- package/dist/_internal.js.map +1 -1
- package/dist/core.d.ts +1464 -0
- package/dist/core.js +744 -0
- package/dist/core.js.map +1 -0
- package/package.json +12 -4
- package/src/_exports/core.ts +1 -0
- package/src/_internal/index.ts +2 -1
- package/src/_internal/render.test.ts +91 -4
- package/src/_internal/render.ts +53 -33
- package/src/core/__tests__/__fixtures__.ts +245 -0
- package/src/core/applications/application-list.test.ts +222 -0
- package/src/core/applications/application-list.ts +103 -0
- package/src/core/applications/application.ts +78 -0
- package/src/core/applications/local-application.test.ts +93 -0
- package/src/core/applications/local-application.ts +56 -0
- package/src/core/canvases.test.ts +38 -0
- package/src/core/canvases.ts +81 -0
- package/src/core/config.ts +34 -0
- package/src/core/index.ts +12 -0
- package/src/{log → core/log}/index.ts +12 -0
- package/src/core/media-libraries.test.ts +38 -0
- package/src/core/media-libraries.ts +83 -0
- package/src/core/organizations.test.ts +134 -0
- package/src/core/organizations.ts +115 -0
- package/src/core/projects.test.ts +248 -0
- package/src/core/projects.ts +114 -0
- package/src/core/shared/urls.test.ts +182 -0
- package/src/core/shared/urls.ts +128 -0
- package/src/core/user-applications/core-app.test.ts +236 -0
- package/src/core/user-applications/core-app.ts +113 -0
- package/src/core/user-applications/studios/index.ts +3 -0
- package/src/core/user-applications/studios/schemas.test.ts +113 -0
- package/src/core/user-applications/studios/schemas.ts +106 -0
- package/src/core/user-applications/studios/studio.test.ts +997 -0
- package/src/core/user-applications/studios/studio.ts +498 -0
- package/src/core/user-applications/studios/workspace.ts +143 -0
- package/src/core/user-applications/user-application.test.ts +125 -0
- package/src/core/user-applications/user-application.ts +107 -0
- package/src/vite-env.d.ts +8 -0
- package/dist/log.d.ts +0 -48
- package/dist/log.js.map +0 -1
- package/src/_exports/log.ts +0 -1
- /package/src/{log → core/log}/index.test.ts +0 -0
package/dist/core.js
ADDED
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createLogger, logger } from "./_chunks-es/index.js";
|
|
3
|
+
import { valid, coerce, gt, lt, gte, rsort } from "semver";
|
|
4
|
+
class AbstractApplication {
|
|
5
|
+
type;
|
|
6
|
+
constructor(type) {
|
|
7
|
+
this.type = type;
|
|
8
|
+
}
|
|
9
|
+
get initials() {
|
|
10
|
+
const SYMBOLS = /[^\p{Alpha}\p{N}\p{White_Space}]/gu, WHITESPACE = new RegExp("\\p{White_Space}+", "u"), ALPHANUMERIC_SEGMENTS = new RegExp("(\\p{N}+|\\p{Alpha}+)", "gu"), IS_NUMERIC = new RegExp("^\\p{N}+$", "u");
|
|
11
|
+
if (!this.title) return "";
|
|
12
|
+
const namesArray = this.title.replace(SYMBOLS, "").split(WHITESPACE).filter(Boolean);
|
|
13
|
+
if (namesArray.length === 0) return "";
|
|
14
|
+
if (namesArray.length === 1) {
|
|
15
|
+
const word = namesArray[0], segments = word.match(ALPHANUMERIC_SEGMENTS) || [];
|
|
16
|
+
return segments.length === 0 ? "" : segments.length === 1 ? word.length === 1 ? word.toUpperCase() : IS_NUMERIC.test(word) ? `${word.charAt(0)}${word.charAt(1)}`.toUpperCase() : word.charAt(0).toUpperCase() : `${segments[0].charAt(0)}${segments[1].charAt(0)}`.toUpperCase();
|
|
17
|
+
}
|
|
18
|
+
return `${namesArray[0].charAt(0)}${namesArray[namesArray.length - 1].charAt(0)}`.toUpperCase();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class LocalApplication extends AbstractApplication {
|
|
22
|
+
localApplication;
|
|
23
|
+
constructor(localApplication) {
|
|
24
|
+
super(localApplication.type), this.localApplication = localApplication;
|
|
25
|
+
}
|
|
26
|
+
get id() {
|
|
27
|
+
const { host, port } = this.localApplication;
|
|
28
|
+
return `${host}-${port}`;
|
|
29
|
+
}
|
|
30
|
+
get title() {
|
|
31
|
+
const { host, port } = this.localApplication;
|
|
32
|
+
return `${host}:${port}`;
|
|
33
|
+
}
|
|
34
|
+
get href() {
|
|
35
|
+
return `/local/${this.id}`;
|
|
36
|
+
}
|
|
37
|
+
get url() {
|
|
38
|
+
const { host, port } = this.localApplication;
|
|
39
|
+
return new URL(`http://${host}:${port}`);
|
|
40
|
+
}
|
|
41
|
+
toProtocolResource() {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"LocalApplication cannot be serialized to a protocol resource"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
class ApplicationList {
|
|
48
|
+
applications;
|
|
49
|
+
/**
|
|
50
|
+
* @param applications - The applications to initialize the list with.
|
|
51
|
+
*/
|
|
52
|
+
constructor(applications) {
|
|
53
|
+
this.applications = [...applications];
|
|
54
|
+
}
|
|
55
|
+
findApplicationsByType(_type) {
|
|
56
|
+
const types = Array.isArray(_type) ? _type : [_type];
|
|
57
|
+
return this.applications.filter((r) => types.includes(r.type));
|
|
58
|
+
}
|
|
59
|
+
findApplication(type, id) {
|
|
60
|
+
switch (type) {
|
|
61
|
+
case "media-library":
|
|
62
|
+
case "canvas":
|
|
63
|
+
return this.applications.find((r) => r.type === type);
|
|
64
|
+
case "coreApp":
|
|
65
|
+
case "studio":
|
|
66
|
+
case "workspace":
|
|
67
|
+
return this.applications.find((r) => r.type === type && r.id === id);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
toProtocolResources() {
|
|
71
|
+
return this.applications.reduce((acc, r) => r.type === "studio" || r instanceof LocalApplication ? acc : [...acc, r.toProtocolResource()], []);
|
|
72
|
+
}
|
|
73
|
+
static areApplicationsEqual(application1, application2) {
|
|
74
|
+
return !application1 || !application2 || application1.type !== application2.type ? !1 : application1.id === application2.id;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const OrganizationId = z.string().nonempty().brand("OrganizationId");
|
|
78
|
+
function brandOrganizationId(id) {
|
|
79
|
+
return OrganizationId.parse(id);
|
|
80
|
+
}
|
|
81
|
+
const OrganizationMember = z.object({
|
|
82
|
+
sanityUserId: z.string(),
|
|
83
|
+
isCurrentUser: z.boolean(),
|
|
84
|
+
user: z.object({
|
|
85
|
+
id: z.string(),
|
|
86
|
+
displayName: z.string(),
|
|
87
|
+
familyName: z.string(),
|
|
88
|
+
givenName: z.string(),
|
|
89
|
+
middleName: z.string().nullable(),
|
|
90
|
+
imageUrl: z.string().nullable(),
|
|
91
|
+
email: z.string(),
|
|
92
|
+
loginProvider: z.string()
|
|
93
|
+
}),
|
|
94
|
+
roles: z.array(
|
|
95
|
+
z.object({
|
|
96
|
+
name: z.string(),
|
|
97
|
+
title: z.string(),
|
|
98
|
+
description: z.string().optional()
|
|
99
|
+
})
|
|
100
|
+
)
|
|
101
|
+
}), Organization = z.object({
|
|
102
|
+
id: OrganizationId,
|
|
103
|
+
name: z.string(),
|
|
104
|
+
slug: z.string().nullable(),
|
|
105
|
+
createdAt: z.string(),
|
|
106
|
+
updatedAt: z.string(),
|
|
107
|
+
dashboardStatus: z.enum(["enabled", "disabled"]),
|
|
108
|
+
aiFeaturesStatus: z.enum(["enabled", "disabled"]),
|
|
109
|
+
defaultRoleName: z.string()
|
|
110
|
+
});
|
|
111
|
+
function parseOrganization(data, options) {
|
|
112
|
+
const includeMembers = options?.includeMembers ?? !0, includeFeatures = options?.includeFeatures ?? !0, extensions = {
|
|
113
|
+
...includeMembers && {
|
|
114
|
+
members: z.array(OrganizationMember)
|
|
115
|
+
},
|
|
116
|
+
...includeFeatures && {
|
|
117
|
+
features: z.array(z.string())
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
return (Object.keys(extensions).length > 0 ? Organization.extend(extensions) : Organization).parse(data);
|
|
121
|
+
}
|
|
122
|
+
const CanvasId = z.string().nonempty().brand("CanvasId");
|
|
123
|
+
function brandCanvasId(id) {
|
|
124
|
+
return CanvasId.parse(id);
|
|
125
|
+
}
|
|
126
|
+
const Canvas = z.object({
|
|
127
|
+
id: CanvasId,
|
|
128
|
+
organizationId: OrganizationId,
|
|
129
|
+
status: z.enum(["active", "provisioning"])
|
|
130
|
+
});
|
|
131
|
+
function parseCanvas(data) {
|
|
132
|
+
return Canvas.parse(data);
|
|
133
|
+
}
|
|
134
|
+
class CanvasApplication extends AbstractApplication {
|
|
135
|
+
canvas;
|
|
136
|
+
constructor(canvas) {
|
|
137
|
+
super("canvas"), this.canvas = canvas;
|
|
138
|
+
}
|
|
139
|
+
get id() {
|
|
140
|
+
return this.canvas.id;
|
|
141
|
+
}
|
|
142
|
+
get href() {
|
|
143
|
+
return "canvas";
|
|
144
|
+
}
|
|
145
|
+
get title() {
|
|
146
|
+
return "Canvas";
|
|
147
|
+
}
|
|
148
|
+
toProtocolResource() {
|
|
149
|
+
return {
|
|
150
|
+
type: "canvas",
|
|
151
|
+
id: this.id
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const MediaLibraryId = z.string().nonempty().brand("MediaLibraryId");
|
|
156
|
+
function brandMediaLibraryId(id) {
|
|
157
|
+
return MediaLibraryId.parse(id);
|
|
158
|
+
}
|
|
159
|
+
const MediaLibrary = z.object({
|
|
160
|
+
id: MediaLibraryId,
|
|
161
|
+
organizationId: OrganizationId,
|
|
162
|
+
status: z.enum(["active", "provisioning"]),
|
|
163
|
+
aclMode: z.enum(["private", "public"])
|
|
164
|
+
});
|
|
165
|
+
function parseMediaLibrary(data) {
|
|
166
|
+
return MediaLibrary.parse(data);
|
|
167
|
+
}
|
|
168
|
+
class MediaLibraryApplication extends AbstractApplication {
|
|
169
|
+
library;
|
|
170
|
+
constructor(library) {
|
|
171
|
+
super("media-library"), this.library = library;
|
|
172
|
+
}
|
|
173
|
+
get id() {
|
|
174
|
+
return this.library.id;
|
|
175
|
+
}
|
|
176
|
+
get href() {
|
|
177
|
+
return "media";
|
|
178
|
+
}
|
|
179
|
+
get title() {
|
|
180
|
+
return "Media Library";
|
|
181
|
+
}
|
|
182
|
+
toProtocolResource() {
|
|
183
|
+
return {
|
|
184
|
+
type: "media-library",
|
|
185
|
+
id: this.id
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const ProjectId = z.string().nonempty().brand("ProjectId");
|
|
190
|
+
function brandProjectId(id) {
|
|
191
|
+
return ProjectId.parse(id);
|
|
192
|
+
}
|
|
193
|
+
const ProjectMember = z.object({
|
|
194
|
+
id: z.string(),
|
|
195
|
+
createdAt: z.string(),
|
|
196
|
+
updatedAt: z.string(),
|
|
197
|
+
isCurrentUser: z.boolean(),
|
|
198
|
+
isRobot: z.boolean(),
|
|
199
|
+
roles: z.array(
|
|
200
|
+
z.object({
|
|
201
|
+
name: z.string(),
|
|
202
|
+
title: z.string(),
|
|
203
|
+
description: z.string()
|
|
204
|
+
})
|
|
205
|
+
)
|
|
206
|
+
}), Project = z.object({
|
|
207
|
+
id: ProjectId,
|
|
208
|
+
displayName: z.string(),
|
|
209
|
+
studioHost: z.string().nullable(),
|
|
210
|
+
organizationId: OrganizationId,
|
|
211
|
+
metadata: z.object({
|
|
212
|
+
color: z.string().optional(),
|
|
213
|
+
externalStudioHost: z.string().optional(),
|
|
214
|
+
initialTemplate: z.string().optional(),
|
|
215
|
+
cliInitializedAt: z.string().optional(),
|
|
216
|
+
integration: z.literal(["manage", "cli"])
|
|
217
|
+
}),
|
|
218
|
+
isBlocked: z.boolean(),
|
|
219
|
+
isDisabled: z.boolean(),
|
|
220
|
+
isDisabledByUser: z.boolean(),
|
|
221
|
+
activityFeedEnabled: z.boolean(),
|
|
222
|
+
createdAt: z.string(),
|
|
223
|
+
updatedAt: z.string()
|
|
224
|
+
});
|
|
225
|
+
function parseProject(data, options) {
|
|
226
|
+
const includeMembers = options?.includeMembers ?? !0, includeFeatures = options?.includeFeatures ?? !0, extensions = {
|
|
227
|
+
...includeMembers && { members: z.array(ProjectMember) },
|
|
228
|
+
...includeFeatures && { features: z.array(z.string()) }
|
|
229
|
+
};
|
|
230
|
+
return (Object.keys(extensions).length > 0 ? Project.extend(extensions) : Project).parse(data);
|
|
231
|
+
}
|
|
232
|
+
const UserApplicationId = z.string().nonempty().brand("UserApplicationId");
|
|
233
|
+
function brandUserApplicationId(id) {
|
|
234
|
+
return UserApplicationId.parse(id);
|
|
235
|
+
}
|
|
236
|
+
const UserApplicationBase = z.object({
|
|
237
|
+
id: UserApplicationId,
|
|
238
|
+
appHost: z.string(),
|
|
239
|
+
urlType: z.enum(["internal", "external"]),
|
|
240
|
+
createdAt: z.string(),
|
|
241
|
+
updatedAt: z.string(),
|
|
242
|
+
dashboardStatus: z.enum(["default", "disabled"])
|
|
243
|
+
}), ActiveDeployment = z.object({
|
|
244
|
+
id: z.string(),
|
|
245
|
+
version: z.string(),
|
|
246
|
+
isActiveDeployment: z.boolean(),
|
|
247
|
+
userApplicationId: z.string(),
|
|
248
|
+
isAutoUpdating: z.boolean(),
|
|
249
|
+
size: z.number(),
|
|
250
|
+
deployedAt: z.string(),
|
|
251
|
+
deployedBy: z.string(),
|
|
252
|
+
createdAt: z.string(),
|
|
253
|
+
updatedAt: z.string()
|
|
254
|
+
});
|
|
255
|
+
class UserApplication extends AbstractApplication {
|
|
256
|
+
application;
|
|
257
|
+
id;
|
|
258
|
+
constructor(application, type) {
|
|
259
|
+
super(type), this.application = application, this.id = brandUserApplicationId(application.id);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* @returns A fully resolved URL instance for the application.
|
|
263
|
+
* For internal applications, constructs a URL using the Sanity studio domain
|
|
264
|
+
* pattern resolved from the environment at the consuming app's build time.
|
|
265
|
+
* For external applications, returns the provided app host as a URL.
|
|
266
|
+
*/
|
|
267
|
+
get url() {
|
|
268
|
+
return this.application.urlType === "internal" ? import.meta.env.VITE_SANITY_ENV === "production" ? new URL(`https://${this.application.appHost}.sanity.studio`) : new URL(
|
|
269
|
+
`https://${this.application.appHost}.studio.${import.meta.env.VITE_SANITY_DOMAIN}`
|
|
270
|
+
) : new URL(this.application.appHost);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const CoreAppUserApplicationManifest = z.object({
|
|
274
|
+
version: z.string(),
|
|
275
|
+
icon: z.string().optional(),
|
|
276
|
+
title: z.string().optional()
|
|
277
|
+
}), CoreAppUserApplicationBase = UserApplicationBase.extend({
|
|
278
|
+
title: z.string(),
|
|
279
|
+
organizationId: OrganizationId,
|
|
280
|
+
type: z.literal("coreApp"),
|
|
281
|
+
manifest: CoreAppUserApplicationManifest.nullable().optional()
|
|
282
|
+
}), InternalCoreAppUserApplication = CoreAppUserApplicationBase.extend({
|
|
283
|
+
urlType: z.literal("internal"),
|
|
284
|
+
activeDeployment: ActiveDeployment.extend({
|
|
285
|
+
manifest: CoreAppUserApplicationManifest.nullable().optional()
|
|
286
|
+
})
|
|
287
|
+
}), ExternalCoreAppUserApplication = CoreAppUserApplicationBase.extend({
|
|
288
|
+
urlType: z.literal("external"),
|
|
289
|
+
activeDeployment: z.null()
|
|
290
|
+
}), CoreAppUserApplication = z.discriminatedUnion("urlType", [
|
|
291
|
+
InternalCoreAppUserApplication,
|
|
292
|
+
ExternalCoreAppUserApplication
|
|
293
|
+
]);
|
|
294
|
+
function parseCoreApplication(data) {
|
|
295
|
+
return CoreAppUserApplication.parse(data);
|
|
296
|
+
}
|
|
297
|
+
class CoreAppApplication extends UserApplication {
|
|
298
|
+
activeDeployment;
|
|
299
|
+
constructor(application) {
|
|
300
|
+
super(application, "coreApp"), this.activeDeployment = application.activeDeployment;
|
|
301
|
+
}
|
|
302
|
+
get href() {
|
|
303
|
+
return `/application/${this.application.id}`;
|
|
304
|
+
}
|
|
305
|
+
get title() {
|
|
306
|
+
return this.activeDeployment?.manifest?.title ?? this.application.title;
|
|
307
|
+
}
|
|
308
|
+
get subtitle() {
|
|
309
|
+
}
|
|
310
|
+
get updatedAt() {
|
|
311
|
+
return this.application.updatedAt;
|
|
312
|
+
}
|
|
313
|
+
get(attr) {
|
|
314
|
+
if (!(attr in this.application))
|
|
315
|
+
throw new Error(
|
|
316
|
+
`Attribute ${attr.toString()} does not exist on application ${this.application.id}`
|
|
317
|
+
);
|
|
318
|
+
return this.application[attr];
|
|
319
|
+
}
|
|
320
|
+
toProtocolResource() {
|
|
321
|
+
return {
|
|
322
|
+
...this.application,
|
|
323
|
+
type: "application",
|
|
324
|
+
url: this.url.toString()
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const Workspace = z.object({
|
|
329
|
+
name: z.string(),
|
|
330
|
+
title: z.string(),
|
|
331
|
+
subtitle: z.string().optional(),
|
|
332
|
+
basePath: z.string(),
|
|
333
|
+
projectId: ProjectId,
|
|
334
|
+
dataset: z.string().optional(),
|
|
335
|
+
icon: z.string().nullable().optional()
|
|
336
|
+
}), ServerManifest = z.object({
|
|
337
|
+
buildId: z.string().optional(),
|
|
338
|
+
bundleVersion: z.string().optional(),
|
|
339
|
+
version: z.string().optional(),
|
|
340
|
+
workspaces: z.array(
|
|
341
|
+
Workspace.extend({
|
|
342
|
+
dataset: z.string(),
|
|
343
|
+
schemaDescriptorId: z.string()
|
|
344
|
+
})
|
|
345
|
+
).optional()
|
|
346
|
+
}), ClientManifest$1 = z.object({
|
|
347
|
+
version: z.number(),
|
|
348
|
+
createdAt: z.string(),
|
|
349
|
+
studioVersion: z.string().optional(),
|
|
350
|
+
workspaces: z.array(
|
|
351
|
+
Workspace.extend({
|
|
352
|
+
schema: z.string(),
|
|
353
|
+
tools: z.string().optional()
|
|
354
|
+
})
|
|
355
|
+
)
|
|
356
|
+
}), StudioUserApplicationBase = UserApplicationBase.extend({
|
|
357
|
+
title: z.string().nullable(),
|
|
358
|
+
projectId: ProjectId,
|
|
359
|
+
type: z.literal("studio"),
|
|
360
|
+
manifest: ClientManifest$1.nullable(),
|
|
361
|
+
manifestData: z.object({ value: ClientManifest$1 }).nullable(),
|
|
362
|
+
autoUpdatingVersion: z.string().nullable(),
|
|
363
|
+
config: z.object({
|
|
364
|
+
"live-manifest": z.object({
|
|
365
|
+
createdAt: z.string(),
|
|
366
|
+
updatedAt: z.string(),
|
|
367
|
+
updatedBy: z.string(),
|
|
368
|
+
value: ServerManifest
|
|
369
|
+
}).optional()
|
|
370
|
+
})
|
|
371
|
+
}), InternalStudioUserApplication = StudioUserApplicationBase.extend({
|
|
372
|
+
urlType: z.literal("internal"),
|
|
373
|
+
activeDeployment: ActiveDeployment.extend({
|
|
374
|
+
manifest: ServerManifest.nullable()
|
|
375
|
+
})
|
|
376
|
+
}), ExternalStudioUserApplication = StudioUserApplicationBase.extend({
|
|
377
|
+
urlType: z.literal("external"),
|
|
378
|
+
activeDeployment: z.null()
|
|
379
|
+
}), StudioUserApplication = z.discriminatedUnion("urlType", [
|
|
380
|
+
InternalStudioUserApplication,
|
|
381
|
+
ExternalStudioUserApplication
|
|
382
|
+
]);
|
|
383
|
+
function parseStudioUserApplication(data) {
|
|
384
|
+
return StudioUserApplication.parse(data);
|
|
385
|
+
}
|
|
386
|
+
const joinUrlPaths = (...paths) => {
|
|
387
|
+
let nextPath = null;
|
|
388
|
+
const safeJoinSegments = (segment1, segment2) => segment1 ? segment2 ? segment1.endsWith("/") && segment2.startsWith("/") ? segment1 + segment2.slice(1) : segment1.endsWith("/") || segment2.startsWith("/") ? segment1 + segment2 : `${segment1}/${segment2}` : segment1 : segment2 || "", validPaths = paths.filter(
|
|
389
|
+
(path) => path != null
|
|
390
|
+
);
|
|
391
|
+
for (const path of validPaths)
|
|
392
|
+
nextPath = safeJoinSegments(
|
|
393
|
+
nextPath,
|
|
394
|
+
path instanceof URL ? path.pathname : path
|
|
395
|
+
);
|
|
396
|
+
return nextPath ?? "";
|
|
397
|
+
};
|
|
398
|
+
function normalizePath(pathname) {
|
|
399
|
+
return pathname.startsWith("/") || (pathname = `/${pathname}`), pathname !== "/" && pathname.endsWith("/") && (pathname = pathname.slice(0, -1)), pathname;
|
|
400
|
+
}
|
|
401
|
+
class StudioWorkspace extends AbstractApplication {
|
|
402
|
+
/**
|
|
403
|
+
* Workspaces always belong to a studio application.
|
|
404
|
+
* They do not exist on their own & therefore can access
|
|
405
|
+
* information about the studio they're in via this property.
|
|
406
|
+
*/
|
|
407
|
+
studioApplication;
|
|
408
|
+
workspace;
|
|
409
|
+
project;
|
|
410
|
+
isDefaultWorkspace;
|
|
411
|
+
constructor(studioApplication, workspace, project, isDefaultWorkspace) {
|
|
412
|
+
super("workspace"), this.studioApplication = studioApplication, this.workspace = workspace, this.project = project, this.isDefaultWorkspace = isDefaultWorkspace;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* The studio application that this workspace belongs to.
|
|
416
|
+
*/
|
|
417
|
+
get studio() {
|
|
418
|
+
return this.studioApplication;
|
|
419
|
+
}
|
|
420
|
+
get id() {
|
|
421
|
+
return StudioWorkspace.makeId(this.studio.id, this.workspace.name);
|
|
422
|
+
}
|
|
423
|
+
get href() {
|
|
424
|
+
return joinUrlPaths(this.studio.href, this.workspace.name);
|
|
425
|
+
}
|
|
426
|
+
get title() {
|
|
427
|
+
return this.isDefaultWorkspace ? this.project.displayName : this.workspace.title;
|
|
428
|
+
}
|
|
429
|
+
get subtitle() {
|
|
430
|
+
return this.isDefaultWorkspace ? (URL.canParse(this.studio.get("appHost")) ? new URL(this.studio.get("appHost")) : this.studio.url).hostname : this.get("subtitle");
|
|
431
|
+
}
|
|
432
|
+
get(attr) {
|
|
433
|
+
return this.workspace[attr];
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* With comlink, studio-applications were not considered applications at all, only workspaces. This is partially why
|
|
437
|
+
* we create default workspaces when the application has no manifest or the manifest has no workspaces.
|
|
438
|
+
*
|
|
439
|
+
* Thereby, to create it we depend on a lot of information from the parent application even if it's duplicated across
|
|
440
|
+
* different workspaces.
|
|
441
|
+
*/
|
|
442
|
+
toProtocolResource() {
|
|
443
|
+
return {
|
|
444
|
+
...this.workspace,
|
|
445
|
+
type: "studio",
|
|
446
|
+
userApplicationId: this.studio.id,
|
|
447
|
+
activeDeployment: this.studio.get("activeDeployment"),
|
|
448
|
+
autoUpdatingVersion: this.studio.get("autoUpdatingVersion"),
|
|
449
|
+
dashboardStatus: this.studio.get("dashboardStatus"),
|
|
450
|
+
url: this.studio.url.toString(),
|
|
451
|
+
href: this.href,
|
|
452
|
+
id: this.id,
|
|
453
|
+
hasManifest: !0,
|
|
454
|
+
hasSchema: !!("schema" in this.workspace && this.workspace.schema),
|
|
455
|
+
manifest: this.studio.get("manifestData")?.value ?? null,
|
|
456
|
+
updatedAt: this.studio.get("updatedAt"),
|
|
457
|
+
version: this.studio.get("activeDeployment")?.version,
|
|
458
|
+
urlType: this.studio.get("urlType"),
|
|
459
|
+
config: this.studio.get("config")
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* @returns the URL to the workspace of a studio application.
|
|
464
|
+
*/
|
|
465
|
+
get url() {
|
|
466
|
+
const studioUrl = new URL(this.studio.url), normalizedUrlPath = normalizePath(studioUrl.pathname);
|
|
467
|
+
let finalBasePath = normalizePath(this.get("basePath"));
|
|
468
|
+
return finalBasePath.startsWith(normalizedUrlPath) && (finalBasePath = finalBasePath.slice(normalizedUrlPath.length)), studioUrl.pathname = joinUrlPaths(normalizedUrlPath, finalBasePath), studioUrl;
|
|
469
|
+
}
|
|
470
|
+
static makeId(applicationId, workspaceName) {
|
|
471
|
+
return `${applicationId}-${workspaceName}`;
|
|
472
|
+
}
|
|
473
|
+
static splitId(id) {
|
|
474
|
+
return id.split(new RegExp(/-(.*)/)).slice(0, 2);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const ClientManifest = ClientManifest$1.superRefine((data, ctx) => {
|
|
478
|
+
data.version || ctx.addIssue({
|
|
479
|
+
code: "invalid_type",
|
|
480
|
+
message: "Manifest version is too old",
|
|
481
|
+
expected: "number"
|
|
482
|
+
}), data.version < 2 && ctx.addIssue({
|
|
483
|
+
code: "invalid_value",
|
|
484
|
+
message: "Manifest version is too old",
|
|
485
|
+
values: [2, 3]
|
|
486
|
+
}), data.version >= 3 && !data.studioVersion && ctx.addIssue({
|
|
487
|
+
code: "invalid_type",
|
|
488
|
+
message: "Manifest version 3 or higher requires a `studioVersion`",
|
|
489
|
+
expected: "string"
|
|
490
|
+
});
|
|
491
|
+
}), DEFAULT_WORKSPACE_DATA = {
|
|
492
|
+
name: "default",
|
|
493
|
+
title: "Default",
|
|
494
|
+
basePath: "/"
|
|
495
|
+
};
|
|
496
|
+
class StudioApplication extends UserApplication {
|
|
497
|
+
/**
|
|
498
|
+
* Returns a list of studio workspaces based on the application manifest.
|
|
499
|
+
* If there is no manifest, or alternatively it is not valid, then we create a default workspace.
|
|
500
|
+
*/
|
|
501
|
+
workspaces = [];
|
|
502
|
+
project;
|
|
503
|
+
/**
|
|
504
|
+
* The projects actually referenced by this studio — the application's own
|
|
505
|
+
* project plus any project used by a workspace. Preserved so the instance
|
|
506
|
+
* can be re-constructed (e.g. by dock wrappers) without having to thread
|
|
507
|
+
* the full organization project list through again.
|
|
508
|
+
*/
|
|
509
|
+
projects;
|
|
510
|
+
/**
|
|
511
|
+
* @param application - The studio application to create a list of workspaces for
|
|
512
|
+
* @param projects - The projects available in the organization. It's not enough to just pass
|
|
513
|
+
* the project that associates with the application because that is the project the app is deployed in relation to.
|
|
514
|
+
* The workspaces may have different projects completely.
|
|
515
|
+
*/
|
|
516
|
+
constructor(application, projects) {
|
|
517
|
+
super(application, "studio");
|
|
518
|
+
let workspaces = [];
|
|
519
|
+
if (this.hasManifest)
|
|
520
|
+
try {
|
|
521
|
+
workspaces = ClientManifest.parse(
|
|
522
|
+
application.manifestData?.value
|
|
523
|
+
).workspaces;
|
|
524
|
+
} catch (error) {
|
|
525
|
+
console.warn(
|
|
526
|
+
`Failed to parse manifest for application ${application.id}`,
|
|
527
|
+
error
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
if (workspaces.length === 0) {
|
|
531
|
+
const serverManifestWorkspaces = application.activeDeployment?.manifest?.workspaces;
|
|
532
|
+
Array.isArray(serverManifestWorkspaces) && serverManifestWorkspaces.length && (workspaces = serverManifestWorkspaces);
|
|
533
|
+
}
|
|
534
|
+
const workspacesWithProjectsMap = workspaces.reduce((acc, workspace) => {
|
|
535
|
+
const project2 = projects.find((p) => p.id === workspace.projectId);
|
|
536
|
+
return project2 ? acc.set(workspace, project2) : console.warn(
|
|
537
|
+
`Project not found for application ${application.id} and workspace ${workspace.name}. This workspace has been omitted.`
|
|
538
|
+
), acc;
|
|
539
|
+
}, /* @__PURE__ */ new Map()), project = projects.find((p) => p.id === application.projectId);
|
|
540
|
+
if (!project)
|
|
541
|
+
throw new Error(`Project not found for application ${application.id}`);
|
|
542
|
+
workspacesWithProjectsMap.size === 0 && workspacesWithProjectsMap.set(
|
|
543
|
+
{
|
|
544
|
+
...DEFAULT_WORKSPACE_DATA,
|
|
545
|
+
projectId: application.projectId
|
|
546
|
+
},
|
|
547
|
+
project
|
|
548
|
+
), this.workspaces = Object.freeze(
|
|
549
|
+
Array.from(workspacesWithProjectsMap.entries()).map(([workspace, p]) => {
|
|
550
|
+
const isDefaultWorkspace = workspace.name === DEFAULT_WORKSPACE_DATA.name && workspace.basePath === DEFAULT_WORKSPACE_DATA.basePath && workspace.title === DEFAULT_WORKSPACE_DATA.title;
|
|
551
|
+
return new StudioWorkspace(this, workspace, p, isDefaultWorkspace);
|
|
552
|
+
})
|
|
553
|
+
), this.project = project;
|
|
554
|
+
const usedProjects = /* @__PURE__ */ new Set([project]);
|
|
555
|
+
for (const workspace of this.workspaces)
|
|
556
|
+
usedProjects.add(workspace.project);
|
|
557
|
+
this.projects = Array.from(usedProjects);
|
|
558
|
+
}
|
|
559
|
+
get href() {
|
|
560
|
+
return `/studio/${this.application.id}`;
|
|
561
|
+
}
|
|
562
|
+
get title() {
|
|
563
|
+
return this.get("title") || (this.workspaces.length > 1 && this.get("urlType") === "internal" ? this.project.displayName : this.workspaces[0].title);
|
|
564
|
+
}
|
|
565
|
+
get subtitle() {
|
|
566
|
+
return new URL(this.url).hostname;
|
|
567
|
+
}
|
|
568
|
+
get(attr) {
|
|
569
|
+
if (!(attr in this.application))
|
|
570
|
+
throw new Error(
|
|
571
|
+
`Attribute ${attr.toString()} does not exist on studio ${this.application.id}`
|
|
572
|
+
);
|
|
573
|
+
return this.application[attr];
|
|
574
|
+
}
|
|
575
|
+
get hasManifest() {
|
|
576
|
+
return typeof this.application.manifestData?.value?.version == "number" && this.application.manifestData?.value?.version >= 2;
|
|
577
|
+
}
|
|
578
|
+
get hasSchema() {
|
|
579
|
+
const workspaces = this.get("manifestData")?.value?.workspaces ?? [];
|
|
580
|
+
return workspaces.length === 0 ? !1 : workspaces.every((w) => !!w.schema);
|
|
581
|
+
}
|
|
582
|
+
resolveVersion() {
|
|
583
|
+
let version;
|
|
584
|
+
if (this.get("urlType") === "internal")
|
|
585
|
+
version = this.get("activeDeployment")?.version;
|
|
586
|
+
else {
|
|
587
|
+
const manifest = this.get("manifestData")?.value;
|
|
588
|
+
version = manifest && "studioVersion" in manifest ? manifest.studioVersion : void 0;
|
|
589
|
+
}
|
|
590
|
+
const bundleVersion = this.get("activeDeployment")?.manifest?.bundleVersion;
|
|
591
|
+
return bundleVersion && valid(coerce(bundleVersion)) && (!version || version && gt(bundleVersion, version)) && (version = bundleVersion), !version || !valid(coerce(version)) ? null : coerce(version);
|
|
592
|
+
}
|
|
593
|
+
get version() {
|
|
594
|
+
const version = this.resolveVersion();
|
|
595
|
+
return version ? version.toString() : null;
|
|
596
|
+
}
|
|
597
|
+
get compatibilityStatus() {
|
|
598
|
+
return StudioApplication.resolveCompatibilityStatus(this);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Used to calculate the compatibility status of a given studio application.
|
|
602
|
+
* Optionally if you've resolved the version elsewhere provide that value to
|
|
603
|
+
* get the new compatibility status without mutating the application.
|
|
604
|
+
*/
|
|
605
|
+
static resolveCompatibilityStatus(application, version = application.version) {
|
|
606
|
+
return version === null || lt(version, StudioApplication.MinimumStudioVersion) ? StudioApplication.CompatibilityStatuses.UNKNOWN : !application.hasSchema || !application.hasManifest || StudioApplication.resolveIssues(application, version).length > 0 ? StudioApplication.CompatibilityStatuses.PARTIALLY_COMPATIBLE : StudioApplication.CompatibilityStatuses.FULLY_COMPATIBLE;
|
|
607
|
+
}
|
|
608
|
+
get isAutoRedirecting() {
|
|
609
|
+
return StudioApplication.resolveIsAutoRedirecting(this);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Mirrors the `isRedirectable` function from Saison defined in
|
|
613
|
+
* https://github.com/sanity-io/saison/blob/83556405d23e07f6d3a71c76249c67e33fe1101f/src/utils/applications.ts
|
|
614
|
+
*
|
|
615
|
+
* Returns whether a studio application is auto-redirecting, meaning it can only be accessed in the context of
|
|
616
|
+
* Dashboard.
|
|
617
|
+
*/
|
|
618
|
+
static resolveIsAutoRedirecting(application, versionArg = application.version) {
|
|
619
|
+
let version = versionArg;
|
|
620
|
+
if (application.get("urlType") === "external" || !version)
|
|
621
|
+
return !1;
|
|
622
|
+
if (!application.get("activeDeployment")?.isAutoUpdating)
|
|
623
|
+
return gt(version, "3.92.0");
|
|
624
|
+
const autoUpdatingVersion = application.get("autoUpdatingVersion");
|
|
625
|
+
if (autoUpdatingVersion) {
|
|
626
|
+
if (["next", "stable", "latest"].includes(autoUpdatingVersion))
|
|
627
|
+
return !0;
|
|
628
|
+
const autoUpdatingVersionPinnedVersion = coerce(autoUpdatingVersion);
|
|
629
|
+
autoUpdatingVersionPinnedVersion && (version = autoUpdatingVersionPinnedVersion);
|
|
630
|
+
}
|
|
631
|
+
return gt(version, StudioApplication.MinimumStudioVersion);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Returns a list of issues that prevent the studio from functioning properly in the Dashboard.
|
|
635
|
+
* This static value depends on the version of the studio that comes from the `activeDeployment` property.
|
|
636
|
+
* As such, if the studio is auto-updating, this list will be incorrect & instead you should use
|
|
637
|
+
* the static method `resolveIssues` to get the correct issues by passing the resolved version.
|
|
638
|
+
*/
|
|
639
|
+
get issues() {
|
|
640
|
+
return StudioApplication.resolveIssues(this);
|
|
641
|
+
}
|
|
642
|
+
static resolveIssues(application, version = application.version) {
|
|
643
|
+
const issues = StudioApplication.Features.filter(
|
|
644
|
+
(feature) => !application.isFeatureSupported(feature.id, version)
|
|
645
|
+
);
|
|
646
|
+
return application.hasManifest || issues.push({
|
|
647
|
+
id: StudioApplication.StudioIssues.ISSUE_MANIFEST
|
|
648
|
+
}), issues;
|
|
649
|
+
}
|
|
650
|
+
isFeatureSupported(feature, version = this.version) {
|
|
651
|
+
const featureVersion = StudioApplication.Features.find(
|
|
652
|
+
(_) => _.id === feature
|
|
653
|
+
)?.version;
|
|
654
|
+
return !featureVersion || !version ? !1 : gte(version, featureVersion);
|
|
655
|
+
}
|
|
656
|
+
toProtocolResource() {
|
|
657
|
+
throw new Error(
|
|
658
|
+
"Studio application resources cannot be converted to protocol resources"
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
static CompatibilityStatuses = {
|
|
662
|
+
UNKNOWN: "unknown",
|
|
663
|
+
PARTIALLY_COMPATIBLE: "partially-compatible",
|
|
664
|
+
FULLY_COMPATIBLE: "fully-compatible"
|
|
665
|
+
};
|
|
666
|
+
static StudioIssues = {
|
|
667
|
+
ISSUE_ACTIVITY: "ACTIVITY",
|
|
668
|
+
ISSUE_AGENT: "AGENT",
|
|
669
|
+
ISSUE_FAVORITES: "FAVORITES",
|
|
670
|
+
ISSUE_URL_SYNCING: "URL_SYNCING",
|
|
671
|
+
ISSUE_UI_ADJUSTMENT: "UI_ADJUSTMENT",
|
|
672
|
+
ISSUE_CONTENT_MAPPING: "CONTENT_MAPPING",
|
|
673
|
+
ISSUE_LOGIN: "LOGIN",
|
|
674
|
+
ISSUE_MANIFEST: "MANIFEST"
|
|
675
|
+
};
|
|
676
|
+
static Features = [
|
|
677
|
+
{
|
|
678
|
+
id: StudioApplication.StudioIssues.ISSUE_AGENT,
|
|
679
|
+
version: "5.1.0"
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
id: StudioApplication.StudioIssues.ISSUE_FAVORITES,
|
|
683
|
+
version: "3.88.1"
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
id: StudioApplication.StudioIssues.ISSUE_ACTIVITY,
|
|
687
|
+
version: "3.88.1"
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
id: StudioApplication.StudioIssues.ISSUE_URL_SYNCING,
|
|
691
|
+
version: "3.75.0"
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
id: StudioApplication.StudioIssues.ISSUE_UI_ADJUSTMENT,
|
|
695
|
+
version: "3.78.1"
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
id: StudioApplication.StudioIssues.ISSUE_CONTENT_MAPPING,
|
|
699
|
+
version: "3.68.0"
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
id: StudioApplication.StudioIssues.ISSUE_LOGIN,
|
|
703
|
+
version: "2.28.0"
|
|
704
|
+
}
|
|
705
|
+
];
|
|
706
|
+
static MinimumStudioVersion = "2.28.0";
|
|
707
|
+
static MinimumStudioVersionWithNoIssues = rsort(
|
|
708
|
+
StudioApplication.Features.map((feature) => feature.version)
|
|
709
|
+
).at(0);
|
|
710
|
+
}
|
|
711
|
+
export {
|
|
712
|
+
AbstractApplication,
|
|
713
|
+
ActiveDeployment,
|
|
714
|
+
ApplicationList,
|
|
715
|
+
CanvasApplication,
|
|
716
|
+
ClientManifest$1 as ClientManifest,
|
|
717
|
+
CoreAppApplication,
|
|
718
|
+
LocalApplication,
|
|
719
|
+
MediaLibraryApplication,
|
|
720
|
+
Organization,
|
|
721
|
+
OrganizationId,
|
|
722
|
+
Project,
|
|
723
|
+
ProjectId,
|
|
724
|
+
StudioApplication,
|
|
725
|
+
StudioUserApplication,
|
|
726
|
+
StudioWorkspace,
|
|
727
|
+
UserApplication,
|
|
728
|
+
UserApplicationBase,
|
|
729
|
+
UserApplicationId,
|
|
730
|
+
brandCanvasId,
|
|
731
|
+
brandMediaLibraryId,
|
|
732
|
+
brandOrganizationId,
|
|
733
|
+
brandProjectId,
|
|
734
|
+
brandUserApplicationId,
|
|
735
|
+
createLogger,
|
|
736
|
+
logger,
|
|
737
|
+
parseCanvas,
|
|
738
|
+
parseCoreApplication,
|
|
739
|
+
parseMediaLibrary,
|
|
740
|
+
parseOrganization,
|
|
741
|
+
parseProject,
|
|
742
|
+
parseStudioUserApplication
|
|
743
|
+
};
|
|
744
|
+
//# sourceMappingURL=core.js.map
|