@sanity/workbench 0.1.0-alpha.2 → 0.1.0-alpha.21
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/README.md +24 -0
- package/dist/_chunks-es/index.js +39 -0
- package/dist/_chunks-es/index.js.map +1 -0
- package/dist/_chunks-es/module-federation.js +59 -0
- package/dist/_chunks-es/module-federation.js.map +1 -0
- package/dist/_chunks-es/studio.js +892 -0
- package/dist/_chunks-es/studio.js.map +1 -0
- package/dist/_internal.d.ts +16 -4
- package/dist/_internal.js +34 -27
- package/dist/_internal.js.map +1 -1
- package/dist/core.d.ts +2250 -0
- package/dist/core.js +74 -0
- package/dist/core.js.map +1 -0
- package/dist/system.d.ts +2135 -0
- package/dist/system.js +887 -0
- package/dist/system.js.map +1 -0
- package/package.json +34 -6
- package/src/_exports/core.ts +1 -0
- package/src/_exports/system.ts +1 -0
- package/src/_internal/index.ts +2 -1
- package/src/_internal/render.ts +72 -43
- package/src/core/applications/application-list.ts +104 -0
- package/src/core/applications/application.ts +177 -0
- package/src/core/applications/interface.ts +126 -0
- package/src/core/canvases.ts +92 -0
- package/src/core/config.ts +34 -0
- package/src/core/env.ts +43 -0
- package/src/core/index.ts +13 -0
- package/src/core/log/index.ts +125 -0
- package/src/core/media-libraries.ts +93 -0
- package/src/core/organizations.ts +115 -0
- package/src/core/projects.ts +114 -0
- package/src/core/shared/urls.ts +129 -0
- package/src/core/user-applications/core-app.ts +148 -0
- package/src/core/user-applications/studios/index.ts +3 -0
- package/src/core/user-applications/studios/schemas.ts +128 -0
- package/src/core/user-applications/studios/studio.ts +533 -0
- package/src/core/user-applications/studios/workspace.ts +156 -0
- package/src/core/user-applications/user-application.ts +222 -0
- package/src/system/auth.machine.ts +223 -0
- package/src/system/index.ts +22 -0
- package/src/system/inspect.ts +40 -0
- package/src/system/load-federated-module.ts +53 -0
- package/src/system/module-federation.ts +116 -0
- package/src/system/remote.machine.ts +219 -0
- package/src/system/remotes.machine.ts +92 -0
- package/src/system/root.machine.ts +224 -0
- package/src/system/service.machine.ts +207 -0
- package/src/system/services.machine.ts +120 -0
- package/src/system/system-preferences.machine.ts +215 -0
- package/src/system/telemetry.machine.ts +179 -0
- package/src/_internal/render.test.ts +0 -18
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Organization ID schema, branded for type safety.
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export const OrganizationId = z.string().nonempty().brand("OrganizationId");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Organization ID type, branded for type safety.
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export type OrganizationId = z.output<typeof OrganizationId>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Validates and brands a string as an OrganizationId.
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export function brandOrganizationId(id: string): OrganizationId {
|
|
20
|
+
return OrganizationId.parse(id);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const OrganizationMember = z.object({
|
|
24
|
+
sanityUserId: z.string(),
|
|
25
|
+
isCurrentUser: z.boolean(),
|
|
26
|
+
user: z.object({
|
|
27
|
+
id: z.string(),
|
|
28
|
+
displayName: z.string(),
|
|
29
|
+
familyName: z.string(),
|
|
30
|
+
givenName: z.string(),
|
|
31
|
+
middleName: z.string().nullable(),
|
|
32
|
+
imageUrl: z.string().nullable(),
|
|
33
|
+
email: z.string(),
|
|
34
|
+
loginProvider: z.string(),
|
|
35
|
+
}),
|
|
36
|
+
roles: z.array(
|
|
37
|
+
z.object({
|
|
38
|
+
name: z.string(),
|
|
39
|
+
title: z.string(),
|
|
40
|
+
description: z.string().optional(),
|
|
41
|
+
}),
|
|
42
|
+
),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export type OrganizationMember = z.output<typeof OrganizationMember>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Organization schema — validates and brands API responses
|
|
52
|
+
* from the `/organizations/:id` endpoint.
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
export const Organization = z.object({
|
|
56
|
+
id: OrganizationId,
|
|
57
|
+
name: z.string(),
|
|
58
|
+
slug: z.string().nullable(),
|
|
59
|
+
createdAt: z.string(),
|
|
60
|
+
updatedAt: z.string(),
|
|
61
|
+
dashboardStatus: z.enum(["enabled", "disabled"]),
|
|
62
|
+
aiFeaturesStatus: z.enum(["enabled", "disabled"]),
|
|
63
|
+
defaultRoleName: z.string().nullable(),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Represents an organization with optional members and
|
|
68
|
+
* features arrays depending on the generic parameters.
|
|
69
|
+
* - `Organization` — base fields only (default)
|
|
70
|
+
* - `Organization<true>` — includes `members`
|
|
71
|
+
* - `Organization<true, true>` — includes both
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
export type Organization<
|
|
75
|
+
IncludeMembers extends boolean = true,
|
|
76
|
+
IncludeFeatures extends boolean = true,
|
|
77
|
+
> = z.output<typeof Organization> &
|
|
78
|
+
(IncludeMembers extends true ? { members: OrganizationMember[] } : unknown) &
|
|
79
|
+
(IncludeFeatures extends true ? { features: string[] } : unknown);
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Validates and parses a raw API response into a branded
|
|
83
|
+
* Organization. The options control which schema is used —
|
|
84
|
+
* matching what the API returns based on query params.
|
|
85
|
+
* @public
|
|
86
|
+
*/
|
|
87
|
+
export function parseOrganization<
|
|
88
|
+
IncludeMembers extends boolean = true,
|
|
89
|
+
IncludeFeatures extends boolean = true,
|
|
90
|
+
>(
|
|
91
|
+
data: unknown,
|
|
92
|
+
options?: {
|
|
93
|
+
includeMembers?: IncludeMembers;
|
|
94
|
+
includeFeatures?: IncludeFeatures;
|
|
95
|
+
},
|
|
96
|
+
): Organization<IncludeMembers, IncludeFeatures> {
|
|
97
|
+
const includeMembers = options?.includeMembers ?? true;
|
|
98
|
+
const includeFeatures = options?.includeFeatures ?? true;
|
|
99
|
+
|
|
100
|
+
const extensions = {
|
|
101
|
+
...(includeMembers && {
|
|
102
|
+
members: z.array(OrganizationMember),
|
|
103
|
+
}),
|
|
104
|
+
...(includeFeatures && {
|
|
105
|
+
features: z.array(z.string()),
|
|
106
|
+
}),
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const schema =
|
|
110
|
+
Object.keys(extensions).length > 0
|
|
111
|
+
? Organization.extend(extensions)
|
|
112
|
+
: Organization;
|
|
113
|
+
|
|
114
|
+
return schema.parse(data) as Organization<IncludeMembers, IncludeFeatures>;
|
|
115
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { OrganizationId } from "./organizations";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Project ID schema, branded for type safety.
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export const ProjectId = z.string().nonempty().brand("ProjectId");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Project ID type, branded for type safety.
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export type ProjectId = z.output<typeof ProjectId>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Validates and brands a string as a ProjectId.
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export function brandProjectId(id: string): ProjectId {
|
|
22
|
+
return ProjectId.parse(id);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ProjectMember = z.object({
|
|
26
|
+
id: z.string(),
|
|
27
|
+
createdAt: z.string(),
|
|
28
|
+
updatedAt: z.string(),
|
|
29
|
+
isCurrentUser: z.boolean(),
|
|
30
|
+
isRobot: z.boolean(),
|
|
31
|
+
roles: z.array(
|
|
32
|
+
z.object({
|
|
33
|
+
name: z.string(),
|
|
34
|
+
title: z.string(),
|
|
35
|
+
description: z.string(),
|
|
36
|
+
}),
|
|
37
|
+
),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export type ProjectMember = z.output<typeof ProjectMember>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Project schema — validates and brands API responses
|
|
47
|
+
* from the `/projects/:id` endpoint.
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
export const Project = z.object({
|
|
51
|
+
id: ProjectId,
|
|
52
|
+
displayName: z.string(),
|
|
53
|
+
studioHost: z.string().nullable(),
|
|
54
|
+
organizationId: OrganizationId,
|
|
55
|
+
metadata: z.object({
|
|
56
|
+
color: z.string().optional(),
|
|
57
|
+
externalStudioHost: z.string().optional(),
|
|
58
|
+
initialTemplate: z.string().optional(),
|
|
59
|
+
cliInitializedAt: z.string().optional(),
|
|
60
|
+
integration: z.string().optional(),
|
|
61
|
+
}),
|
|
62
|
+
isBlocked: z.boolean(),
|
|
63
|
+
isDisabled: z.boolean(),
|
|
64
|
+
isDisabledByUser: z.boolean(),
|
|
65
|
+
activityFeedEnabled: z.boolean(),
|
|
66
|
+
createdAt: z.string(),
|
|
67
|
+
updatedAt: z.string(),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Represents a Sanity project with optional members and
|
|
72
|
+
* features arrays depending on the generic parameters.
|
|
73
|
+
* By default, neither members nor features are included.
|
|
74
|
+
* - `Project` — base fields only (default)
|
|
75
|
+
* - `Project<true>` — includes `members`
|
|
76
|
+
* - `Project<true, true>` — includes both
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export type Project<
|
|
80
|
+
IncludeMembers extends boolean = true,
|
|
81
|
+
IncludeFeatures extends boolean = true,
|
|
82
|
+
> = z.output<typeof Project> &
|
|
83
|
+
(IncludeMembers extends true ? { members: ProjectMember[] } : unknown) &
|
|
84
|
+
(IncludeFeatures extends true ? { features: string[] } : unknown);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validates and parses a raw API response into a branded
|
|
88
|
+
* Project. The options control which schema is used —
|
|
89
|
+
* matching what the API returns based on query params.
|
|
90
|
+
* @public
|
|
91
|
+
*/
|
|
92
|
+
export function parseProject<
|
|
93
|
+
IncludeMembers extends boolean = true,
|
|
94
|
+
IncludeFeatures extends boolean = true,
|
|
95
|
+
>(
|
|
96
|
+
data: unknown,
|
|
97
|
+
options?: {
|
|
98
|
+
includeMembers?: IncludeMembers;
|
|
99
|
+
includeFeatures?: IncludeFeatures;
|
|
100
|
+
},
|
|
101
|
+
): Project<IncludeMembers, IncludeFeatures> {
|
|
102
|
+
const includeMembers = options?.includeMembers ?? true;
|
|
103
|
+
const includeFeatures = options?.includeFeatures ?? true;
|
|
104
|
+
|
|
105
|
+
const extensions = {
|
|
106
|
+
...(includeMembers && { members: z.array(ProjectMember) }),
|
|
107
|
+
...(includeFeatures && { features: z.array(z.string()) }),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const schema =
|
|
111
|
+
Object.keys(extensions).length > 0 ? Project.extend(extensions) : Project;
|
|
112
|
+
|
|
113
|
+
return schema.parse(data) as Project<IncludeMembers, IncludeFeatures>;
|
|
114
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Joins multiple path segments into a single path string.
|
|
3
|
+
* Handles null, undefined, and URL objects gracefully.
|
|
4
|
+
*
|
|
5
|
+
* @public
|
|
6
|
+
* @param paths - An array of path segments to join.
|
|
7
|
+
* @returns A single joined path string.
|
|
8
|
+
*/
|
|
9
|
+
export const joinUrlPaths = (
|
|
10
|
+
...paths: Array<string | URL | null | undefined>
|
|
11
|
+
): string => {
|
|
12
|
+
let nextPath = null;
|
|
13
|
+
|
|
14
|
+
const safeJoinSegments = (
|
|
15
|
+
segment1: string | null | undefined,
|
|
16
|
+
segment2: string | null | undefined,
|
|
17
|
+
): string => {
|
|
18
|
+
if (!segment1) {
|
|
19
|
+
if (!segment2) {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return segment2;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!segment2) {
|
|
27
|
+
return segment1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (segment1.endsWith("/") && segment2.startsWith("/")) {
|
|
31
|
+
return segment1 + segment2.slice(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (segment1.endsWith("/") || segment2.startsWith("/")) {
|
|
35
|
+
return segment1 + segment2;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return `${segment1}/${segment2}`;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const validPaths = paths.filter(
|
|
42
|
+
(path) => path !== null && path !== undefined,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
for (const path of validPaths) {
|
|
46
|
+
nextPath = safeJoinSegments(
|
|
47
|
+
nextPath,
|
|
48
|
+
path instanceof URL ? path.pathname : path,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return nextPath ?? "";
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Returns a normalized path by ensuring it starts with a single leading slash
|
|
57
|
+
* and does not end with a trailing slash (unless it's the root path).
|
|
58
|
+
*/
|
|
59
|
+
export function normalizePath(pathname: string): string {
|
|
60
|
+
if (!pathname.startsWith("/")) {
|
|
61
|
+
pathname = `/${pathname}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (pathname !== "/" && pathname.endsWith("/")) {
|
|
65
|
+
pathname = pathname.slice(0, -1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return pathname;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The pathname, search, and hash values of a URL.
|
|
73
|
+
*/
|
|
74
|
+
interface Path {
|
|
75
|
+
/**
|
|
76
|
+
* A URL pathname, beginning with a /.
|
|
77
|
+
*/
|
|
78
|
+
pathname: string;
|
|
79
|
+
/**
|
|
80
|
+
* A URL search string, beginning with a ?.
|
|
81
|
+
*/
|
|
82
|
+
search: string;
|
|
83
|
+
/**
|
|
84
|
+
* A URL fragment identifier, beginning with a #.
|
|
85
|
+
*/
|
|
86
|
+
hash: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Parses a string URL path into its separate pathname, search, and hash components.
|
|
91
|
+
*/
|
|
92
|
+
export function parsePath(path: string): Partial<Path> {
|
|
93
|
+
let parsedPath: Partial<Path> = {};
|
|
94
|
+
|
|
95
|
+
if (path) {
|
|
96
|
+
let hashIndex = path.indexOf("#");
|
|
97
|
+
if (hashIndex >= 0) {
|
|
98
|
+
parsedPath.hash = path.substring(hashIndex);
|
|
99
|
+
path = path.substring(0, hashIndex);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let searchIndex = path.indexOf("?");
|
|
103
|
+
if (searchIndex >= 0) {
|
|
104
|
+
parsedPath.search = path.substring(searchIndex);
|
|
105
|
+
path = path.substring(0, searchIndex);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (path) {
|
|
109
|
+
parsedPath.pathname = path;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return parsedPath;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Creates a string URL path from the given pathname, search, and hash components.
|
|
118
|
+
*/
|
|
119
|
+
export function createPath({
|
|
120
|
+
pathname = "/",
|
|
121
|
+
search = "",
|
|
122
|
+
hash = "",
|
|
123
|
+
}: Partial<Path>) {
|
|
124
|
+
if (search && search !== "?")
|
|
125
|
+
pathname += search.charAt(0) === "?" ? search : `?${search}`;
|
|
126
|
+
if (hash && hash !== "#")
|
|
127
|
+
pathname += hash.charAt(0) === "#" ? hash : `#${hash}`;
|
|
128
|
+
return pathname;
|
|
129
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// eslint-disable-next-line no-restricted-imports
|
|
2
|
+
import type { ApplicationResource as ProtocolApplicationResource } from "@sanity/message-protocol";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { OrganizationId } from "../organizations";
|
|
6
|
+
import {
|
|
7
|
+
UserApplication,
|
|
8
|
+
UserApplicationBase,
|
|
9
|
+
ActiveDeployment,
|
|
10
|
+
} from "./user-application";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export const CoreAppUserApplicationManifest = z.object({
|
|
16
|
+
version: z.string(),
|
|
17
|
+
icon: z.string().optional(),
|
|
18
|
+
title: z.string().optional(),
|
|
19
|
+
group: z.string().optional(),
|
|
20
|
+
priority: z.number().optional(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export type CoreAppUserApplicationManifest = z.output<
|
|
27
|
+
typeof CoreAppUserApplicationManifest
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
const CoreAppUserApplicationBase = UserApplicationBase.extend({
|
|
31
|
+
title: z.string(),
|
|
32
|
+
organizationId: OrganizationId,
|
|
33
|
+
type: z.literal("coreApp"),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Core apps surface their manifest exclusively via `activeDeployment.manifest`
|
|
37
|
+
// — they have no top-level `manifest` field and no `manifestData`.
|
|
38
|
+
const CoreAppActiveDeployment = ActiveDeployment.extend({
|
|
39
|
+
manifest: CoreAppUserApplicationManifest.nullable(),
|
|
40
|
+
}).nullable();
|
|
41
|
+
|
|
42
|
+
const InternalCoreAppUserApplication = CoreAppUserApplicationBase.extend({
|
|
43
|
+
urlType: z.literal("internal"),
|
|
44
|
+
activeDeployment: CoreAppActiveDeployment,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const ExternalCoreAppUserApplication = CoreAppUserApplicationBase.extend({
|
|
48
|
+
urlType: z.literal("external"),
|
|
49
|
+
activeDeployment: CoreAppActiveDeployment,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Core application schema — validates and brands API
|
|
54
|
+
* responses from the `/user-applications?appType=coreApp`
|
|
55
|
+
* endpoint. Uses a discriminated union on `urlType`.
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
const CoreAppUserApplication = z.discriminatedUnion("urlType", [
|
|
59
|
+
InternalCoreAppUserApplication,
|
|
60
|
+
ExternalCoreAppUserApplication,
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
export type CoreAppUserApplication = z.output<typeof CoreAppUserApplication>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validates and parses a raw API response into a branded
|
|
70
|
+
* CoreAppUserApplicationData.
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
export function parseCoreApplication(data: unknown): CoreAppUserApplication {
|
|
74
|
+
return CoreAppUserApplication.parse(data);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
export class CoreAppApplication extends UserApplication<
|
|
81
|
+
CoreAppUserApplication,
|
|
82
|
+
"coreApp",
|
|
83
|
+
ProtocolApplicationResource
|
|
84
|
+
> {
|
|
85
|
+
readonly activeDeployment: CoreAppUserApplication["activeDeployment"];
|
|
86
|
+
|
|
87
|
+
constructor(
|
|
88
|
+
application: CoreAppUserApplication,
|
|
89
|
+
options: {
|
|
90
|
+
isLocal?: boolean;
|
|
91
|
+
remoteApplication?: CoreAppApplication | null;
|
|
92
|
+
} = {},
|
|
93
|
+
) {
|
|
94
|
+
super(application, "coreApp", options);
|
|
95
|
+
|
|
96
|
+
this.activeDeployment = application.activeDeployment;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Typed `string | null` so the UI-aware remote subclass can override it to
|
|
100
|
+
// `null` for a non-navigable app (US5, spec 002-workbench-extension-api); the core route itself is always set.
|
|
101
|
+
get href(): string | null {
|
|
102
|
+
return this.isLocal
|
|
103
|
+
? `/local/${this.id}`
|
|
104
|
+
: `/application/${this.application.id}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
get title() {
|
|
108
|
+
return this.manifest?.title ?? this.application.title;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Resolves the core app's manifest from `activeDeployment.manifest`. This
|
|
113
|
+
* is the canonical (and only) slot for core app manifests — both for
|
|
114
|
+
* Sanity-deployed apps and for local CLI dev-server apps, which synthesise
|
|
115
|
+
* a deployment to surface the live manifest.
|
|
116
|
+
*/
|
|
117
|
+
get manifest(): CoreAppUserApplicationManifest | null {
|
|
118
|
+
return this.activeDeployment?.manifest ?? null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get subtitle() {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
get updatedAt() {
|
|
126
|
+
return this.application.updatedAt;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get<TKey extends keyof CoreAppUserApplication>(
|
|
130
|
+
attr: TKey,
|
|
131
|
+
): CoreAppUserApplication[TKey] {
|
|
132
|
+
if (!(attr in this.application)) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Attribute ${attr.toString()} does not exist on application ${this.application.id}`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return this.application[attr];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
toProtocolResource(): ProtocolApplicationResource {
|
|
142
|
+
return {
|
|
143
|
+
...this.application,
|
|
144
|
+
type: "application",
|
|
145
|
+
url: this.url.toString(),
|
|
146
|
+
} as ProtocolApplicationResource;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { ProjectId } from "../../projects";
|
|
4
|
+
import { ActiveDeployment, UserApplicationBase } from "../user-application";
|
|
5
|
+
|
|
6
|
+
const Workspace = z.object({
|
|
7
|
+
name: z.string(),
|
|
8
|
+
title: z.string(),
|
|
9
|
+
subtitle: z.string().optional(),
|
|
10
|
+
basePath: z.string(),
|
|
11
|
+
projectId: ProjectId,
|
|
12
|
+
dataset: z.string().optional(),
|
|
13
|
+
icon: z.string().nullable().optional(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export type Workspace = z.output<typeof Workspace>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
export const ServerManifest = z.object({
|
|
25
|
+
buildId: z.string().optional(),
|
|
26
|
+
bundleVersion: z.string().optional(),
|
|
27
|
+
version: z.string().optional(),
|
|
28
|
+
group: z.string().optional(),
|
|
29
|
+
priority: z.number().optional(),
|
|
30
|
+
workspaces: z
|
|
31
|
+
.array(
|
|
32
|
+
Workspace.extend({
|
|
33
|
+
dataset: z.string(),
|
|
34
|
+
schemaDescriptorId: z.string(),
|
|
35
|
+
}),
|
|
36
|
+
)
|
|
37
|
+
.optional(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export type ServerManifest = z.output<typeof ServerManifest>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export const ClientManifest = z.object({
|
|
49
|
+
version: z.number(),
|
|
50
|
+
createdAt: z.string(),
|
|
51
|
+
studioVersion: z.string().optional(),
|
|
52
|
+
group: z.string().optional(),
|
|
53
|
+
priority: z.number().optional(),
|
|
54
|
+
workspaces: z.array(
|
|
55
|
+
Workspace.extend({
|
|
56
|
+
schema: z.string(),
|
|
57
|
+
tools: z.string().optional(),
|
|
58
|
+
}),
|
|
59
|
+
),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
export type ClientManifest = z.output<typeof ClientManifest>;
|
|
66
|
+
|
|
67
|
+
const StudioUserApplicationBase = UserApplicationBase.extend({
|
|
68
|
+
title: z.string().nullable(),
|
|
69
|
+
projectId: ProjectId,
|
|
70
|
+
type: z.literal("studio"),
|
|
71
|
+
/**
|
|
72
|
+
* @deprecated Use `manifestData` instead.
|
|
73
|
+
*/
|
|
74
|
+
manifest: ClientManifest.nullable(),
|
|
75
|
+
manifestData: z.object({ value: ClientManifest }).nullable(),
|
|
76
|
+
autoUpdatingVersion: z.string().nullable(),
|
|
77
|
+
config: z.object({
|
|
78
|
+
"live-manifest": z
|
|
79
|
+
.object({
|
|
80
|
+
createdAt: z.string(),
|
|
81
|
+
updatedAt: z.string(),
|
|
82
|
+
updatedBy: z.string(),
|
|
83
|
+
value: ServerManifest,
|
|
84
|
+
})
|
|
85
|
+
.optional(),
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const InternalStudioUserApplication = StudioUserApplicationBase.extend({
|
|
90
|
+
urlType: z.literal("internal"),
|
|
91
|
+
activeDeployment: ActiveDeployment.extend({
|
|
92
|
+
manifest: ServerManifest.nullable(),
|
|
93
|
+
}).nullable(),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const ExternalStudioUserApplication = StudioUserApplicationBase.extend({
|
|
97
|
+
urlType: z.literal("external"),
|
|
98
|
+
activeDeployment: ActiveDeployment.extend({
|
|
99
|
+
manifest: ServerManifest.nullable(),
|
|
100
|
+
}).nullable(),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Studio user application schema — validates and brands API
|
|
105
|
+
* responses from the `/user-applications?appType=studio`
|
|
106
|
+
* endpoint. Uses a discriminated union on `urlType`.
|
|
107
|
+
* @public
|
|
108
|
+
*/
|
|
109
|
+
export const StudioUserApplication = z.discriminatedUnion("urlType", [
|
|
110
|
+
InternalStudioUserApplication,
|
|
111
|
+
ExternalStudioUserApplication,
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @public
|
|
116
|
+
*/
|
|
117
|
+
export type StudioUserApplication = z.output<typeof StudioUserApplication>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Validates and parses a raw API response into a branded
|
|
121
|
+
* StudioUserApplication.
|
|
122
|
+
* @public
|
|
123
|
+
*/
|
|
124
|
+
export function parseStudioUserApplication(
|
|
125
|
+
data: unknown,
|
|
126
|
+
): StudioUserApplication {
|
|
127
|
+
return StudioUserApplication.parse(data);
|
|
128
|
+
}
|