@sanity/workbench 0.1.0-alpha.7 → 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.
@@ -0,0 +1,143 @@
1
+ import type { StudioResource as ProtocolStudioResource } from "@sanity/message-protocol";
2
+
3
+ import { AbstractApplication } from "../../applications/application";
4
+ import type { Project } from "../../projects";
5
+ import { joinUrlPaths, normalizePath } from "../../shared/urls";
6
+ import type { UserApplicationId } from "../user-application";
7
+ import type { Workspace } from "./schemas";
8
+ import type { StudioApplication } from "./studio";
9
+
10
+ /**
11
+ * @internal
12
+ */
13
+ export class StudioWorkspace extends AbstractApplication<
14
+ "workspace",
15
+ ProtocolStudioResource
16
+ > {
17
+ /**
18
+ * Workspaces always belong to a studio application.
19
+ * They do not exist on their own & therefore can access
20
+ * information about the studio they're in via this property.
21
+ */
22
+ private readonly studioApplication: StudioApplication;
23
+ private readonly workspace: Workspace;
24
+ readonly project: Project<false, false>;
25
+ private readonly isDefaultWorkspace: boolean;
26
+
27
+ constructor(
28
+ studioApplication: StudioApplication,
29
+ workspace: Workspace,
30
+ project: Project<false, false>,
31
+ isDefaultWorkspace: boolean,
32
+ ) {
33
+ super("workspace");
34
+
35
+ this.studioApplication = studioApplication;
36
+ this.workspace = workspace;
37
+ this.project = project;
38
+ this.isDefaultWorkspace = isDefaultWorkspace;
39
+ }
40
+
41
+ /**
42
+ * The studio application that this workspace belongs to.
43
+ */
44
+ get studio() {
45
+ return this.studioApplication;
46
+ }
47
+
48
+ get id() {
49
+ return StudioWorkspace.makeId(this.studio.id, this.workspace.name);
50
+ }
51
+
52
+ get href() {
53
+ return joinUrlPaths(this.studio.href, this.workspace.name);
54
+ }
55
+
56
+ get title() {
57
+ /**
58
+ * If there's no manifest we will have created a single workspace for the application.
59
+ * In this circumstance we won't have a meaningful title, so instead we use the hostname of the appHost.
60
+ */
61
+ if (this.isDefaultWorkspace) {
62
+ return this.project.displayName;
63
+ }
64
+
65
+ return this.workspace.title;
66
+ }
67
+
68
+ get subtitle() {
69
+ if (this.isDefaultWorkspace) {
70
+ const isValidAppHost = URL.canParse(this.studio.get("appHost"));
71
+ const url = isValidAppHost
72
+ ? new URL(this.studio.get("appHost"))
73
+ : this.studio.url;
74
+
75
+ return url.hostname;
76
+ }
77
+
78
+ return this.get("subtitle");
79
+ }
80
+
81
+ get<TKey extends Exclude<keyof Workspace, "id" | "icon" | "title">>(
82
+ attr: TKey,
83
+ ): Workspace[TKey] {
84
+ return this.workspace[attr];
85
+ }
86
+
87
+ /**
88
+ * With comlink, studio-applications were not considered applications at all, only workspaces. This is partially why
89
+ * we create default workspaces when the application has no manifest or the manifest has no workspaces.
90
+ *
91
+ * Thereby, to create it we depend on a lot of information from the parent application even if it's duplicated across
92
+ * different workspaces.
93
+ */
94
+ toProtocolResource(): ProtocolStudioResource {
95
+ return {
96
+ ...this.workspace,
97
+ type: "studio",
98
+ userApplicationId: this.studio.id,
99
+ activeDeployment: this.studio.get("activeDeployment"),
100
+ autoUpdatingVersion: this.studio.get("autoUpdatingVersion"),
101
+ dashboardStatus: this.studio.get("dashboardStatus"),
102
+ url: this.studio.url.toString(),
103
+ href: this.href,
104
+ id: this.id,
105
+ hasManifest: true,
106
+ hasSchema: Boolean("schema" in this.workspace && this.workspace.schema),
107
+ manifest: (this.studio.get("manifestData")?.value ??
108
+ null) as ProtocolStudioResource["manifest"],
109
+ updatedAt: this.studio.get("updatedAt"),
110
+ version: this.studio.get("activeDeployment")?.version,
111
+ urlType: this.studio.get("urlType"),
112
+ config: this.studio.get("config"),
113
+ } satisfies ProtocolStudioResource;
114
+ }
115
+
116
+ /**
117
+ * @returns the URL to the workspace of a studio application.
118
+ */
119
+ get url(): URL {
120
+ const studioUrl = new URL(this.studio.url);
121
+ const normalizedUrlPath = normalizePath(studioUrl.pathname);
122
+
123
+ let finalBasePath = normalizePath(this.get("basePath"));
124
+
125
+ // the appHost may already contain the basepath for externally hosted studios
126
+ // or embedded studios, so we deduplicate the segments
127
+ if (finalBasePath.startsWith(normalizedUrlPath)) {
128
+ finalBasePath = finalBasePath.slice(normalizedUrlPath.length);
129
+ }
130
+
131
+ studioUrl.pathname = joinUrlPaths(normalizedUrlPath, finalBasePath);
132
+
133
+ return studioUrl;
134
+ }
135
+
136
+ static makeId(applicationId: UserApplicationId, workspaceName: string) {
137
+ return `${applicationId}-${workspaceName}`;
138
+ }
139
+
140
+ static splitId(id: string): [string, string] {
141
+ return id.split(new RegExp(/-(.*)/)).slice(0, 2) as [string, string];
142
+ }
143
+ }
@@ -1,18 +1,17 @@
1
- import type {
2
- ApplicationResource as ProtocolApplicationResource,
3
- CoreApplication,
4
- } from "@sanity/message-protocol";
1
+ // eslint-disable-next-line no-restricted-imports
2
+ import type { ApplicationResource as ProtocolApplicationResource } from "@sanity/message-protocol";
5
3
  import { afterEach, describe, expect, it, vi } from "vitest";
6
4
 
7
5
  import { APPLICATION_DATA } from "../__tests__/__fixtures__";
6
+ import type { CoreAppUserApplication } from "./core-app";
8
7
  import { UserApplication as AbstractUserApplication } from "./user-application";
9
8
 
10
9
  class UserApplication extends AbstractUserApplication<
11
- CoreApplication,
10
+ CoreAppUserApplication,
12
11
  "coreApp",
13
12
  ProtocolApplicationResource
14
13
  > {
15
- constructor(application: CoreApplication) {
14
+ constructor(application: CoreAppUserApplication) {
16
15
  super(application, "coreApp");
17
16
  }
18
17
 
@@ -37,11 +36,11 @@ class UserApplication extends AbstractUserApplication<
37
36
  }
38
37
  }
39
38
 
40
- const setup = (overrides?: Partial<CoreApplication>) =>
39
+ const setup = (overrides?: Partial<CoreAppUserApplication>) =>
41
40
  new UserApplication({
42
41
  ...APPLICATION_DATA,
43
42
  ...overrides,
44
- } as CoreApplication);
43
+ } as CoreAppUserApplication);
45
44
 
46
45
  describe("UserApplication", () => {
47
46
  afterEach(() => {
@@ -1,7 +1,5 @@
1
- import type {
2
- Resource as ProtocolResource,
3
- UserApplication as UserApplicationData,
4
- } from "@sanity/message-protocol";
1
+ // eslint-disable-next-line no-restricted-imports
2
+ import type { Resource as ProtocolResource } from "@sanity/message-protocol";
5
3
  import { z } from "zod";
6
4
 
7
5
  import {
@@ -32,11 +30,44 @@ export function brandUserApplicationId(id: string): UserApplicationId {
32
30
  return UserApplicationId.parse(id);
33
31
  }
34
32
 
33
+ /**
34
+ * @public
35
+ */
36
+ export const UserApplicationBase = z.object({
37
+ id: UserApplicationId,
38
+ appHost: z.string(),
39
+ urlType: z.enum(["internal", "external"]),
40
+ createdAt: z.string(),
41
+ updatedAt: z.string(),
42
+ dashboardStatus: z.enum(["default", "disabled"]),
43
+ });
44
+
45
+ /**
46
+ * @public
47
+ */
48
+ export type UserApplicationBase = z.output<typeof UserApplicationBase>;
49
+
50
+ /**
51
+ * @public
52
+ */
53
+ export const ActiveDeployment = z.object({
54
+ id: z.string(),
55
+ version: z.string(),
56
+ isActiveDeployment: z.boolean(),
57
+ userApplicationId: z.string(),
58
+ isAutoUpdating: z.boolean(),
59
+ size: z.number(),
60
+ deployedAt: z.string(),
61
+ deployedBy: z.string(),
62
+ createdAt: z.string(),
63
+ updatedAt: z.string(),
64
+ });
65
+
35
66
  /**
36
67
  * @internal
37
68
  */
38
69
  export abstract class UserApplication<
39
- TUserApplication extends UserApplicationData,
70
+ TUserApplication extends UserApplicationBase,
40
71
  TType extends Extract<AbstractApplicationType, "coreApp" | "studio">,
41
72
  TProtocolResource extends ProtocolResource = Extract<
42
73
  ProtocolResource,