@sanity/workbench 0.1.0-alpha.6 → 0.1.0-alpha.7
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} +1 -1
- package/dist/_chunks-es/index.js.map +1 -0
- package/dist/_internal.d.ts +13 -6
- package/dist/_internal.js +19 -3
- package/dist/_internal.js.map +1 -1
- package/dist/core.d.ts +906 -0
- package/dist/core.js +642 -0
- package/dist/core.js.map +1 -0
- package/package.json +10 -4
- package/src/_exports/core.ts +1 -0
- package/src/_internal/index.ts +2 -1
- package/src/_internal/render.test.ts +56 -1
- package/src/_internal/render.ts +51 -24
- package/src/core/__tests__/__fixtures__.ts +248 -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 +52 -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/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 +151 -0
- package/src/core/user-applications/core-app.ts +57 -0
- package/src/core/user-applications/studio.test.ts +928 -0
- package/src/core/user-applications/studio.ts +656 -0
- package/src/core/user-applications/user-application.test.ts +126 -0
- package/src/core/user-applications/user-application.ts +76 -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/src/{log → core/log}/index.ts +0 -0
package/dist/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sources":["../src/core/applications/application.ts","../src/core/applications/local-application.ts","../src/core/applications/application-list.ts","../src/core/organizations.ts","../src/core/canvases.ts","../src/core/media-libraries.ts","../src/core/projects.ts","../src/core/user-applications/user-application.ts","../src/core/user-applications/core-app.ts","../src/core/shared/urls.ts","../src/core/user-applications/studio.ts"],"sourcesContent":["import type { Resource as ProtocolResource } from \"@sanity/message-protocol\";\n\n/**\n * @public\n */\nexport type AbstractApplicationType =\n | \"studio\"\n | \"coreApp\"\n | \"canvas\"\n | \"media-library\"\n | \"workspace\";\n\n/**\n * @public\n */\nexport abstract class AbstractApplication<\n TType extends AbstractApplicationType,\n TProtocolResource extends ProtocolResource = Extract<\n ProtocolResource,\n { type: TType }\n >,\n> {\n readonly type: TType;\n\n constructor(type: TType) {\n this.type = type;\n }\n\n abstract get href(): string;\n\n abstract get title(): string;\n\n abstract get id(): string;\n\n get initials(): string {\n const SYMBOLS = /[^\\p{Alpha}\\p{N}\\p{White_Space}]/gu;\n const WHITESPACE = /\\p{White_Space}+/u;\n const ALPHANUMERIC_SEGMENTS = /(\\p{N}+|\\p{Alpha}+)/gu;\n const IS_NUMERIC = /^\\p{N}+$/u;\n\n if (!this.title) return \"\";\n\n const namesArray = this.title\n .replace(SYMBOLS, \"\")\n .split(WHITESPACE)\n .filter(Boolean);\n\n if (namesArray.length === 0) return \"\";\n\n if (namesArray.length === 1) {\n const word = namesArray[0];\n const segments = word.match(ALPHANUMERIC_SEGMENTS) || [];\n\n if (segments.length === 0) return \"\";\n if (segments.length === 1) {\n if (word.length === 1) {\n return word.toUpperCase();\n }\n\n if (IS_NUMERIC.test(word)) {\n return `${word.charAt(0)}${word.charAt(1)}`.toUpperCase();\n }\n\n return word.charAt(0).toUpperCase();\n }\n\n return `${segments[0]!.charAt(0)}${segments[1].charAt(0)}`.toUpperCase();\n }\n\n return `${namesArray[0].charAt(0)}${namesArray[namesArray.length - 1].charAt(0)}`.toUpperCase();\n }\n\n /**\n * Converts the resource to a protocol resource that comlink expects\n * for backwards compatibility with the old API format.\n */\n abstract toProtocolResource(): TProtocolResource;\n}\n","import { AbstractApplication } from \"./application\";\n\n/**\n * Raw data for a local application discovered by the CLI dev server.\n *\n * @experimental\n * @public\n */\nexport interface LocalApplicationData {\n host: string;\n port: number;\n type: \"studio\" | \"coreApp\";\n}\n\n/**\n * An Application wrapping a locally-discovered CLI application.\n * Provides a deterministic id, title and href suitable for use in navigation.\n *\n * @internal\n */\nexport class LocalApplication extends AbstractApplication<\n \"studio\" | \"coreApp\",\n never\n> {\n readonly localApplication: LocalApplicationData;\n\n constructor(localApplication: LocalApplicationData) {\n super(localApplication.type);\n this.localApplication = localApplication;\n }\n\n get id(): string {\n const { host, port } = this.localApplication;\n return `local-${host}-${port}`;\n }\n\n get title(): string {\n const { host, port } = this.localApplication;\n return `${host}:${port}`;\n }\n\n get href(): string {\n const { host, port } = this.localApplication;\n return `/local/${host}:${port}`;\n }\n\n toProtocolResource(): never {\n throw new Error(\n \"LocalApplication cannot be serialized to a protocol resource\",\n );\n }\n}\n","import type { Resource as ProtocolResource } from \"@sanity/message-protocol\";\n\nimport type { CanvasApplication } from \"../canvases\";\nimport type { MediaLibraryApplication } from \"../media-libraries\";\nimport type { CoreAppApplication } from \"../user-applications/core-app\";\nimport type {\n StudioApplication,\n StudioWorkspace,\n} from \"../user-applications/studio\";\nimport type { UserApplicationId } from \"../user-applications/user-application\";\nimport { LocalApplication } from \"./local-application\";\n\ntype Application =\n | CoreAppApplication\n | StudioApplication\n | StudioWorkspace\n | CanvasApplication\n | MediaLibraryApplication\n | LocalApplication;\n\n/**\n * @public\n */\nexport class ApplicationList<TApplication extends Application = Application> {\n readonly applications: TApplication[];\n\n /**\n * @param applications - The applications to initialize the list with.\n */\n constructor(applications: TApplication[]) {\n this.applications = [...applications];\n }\n\n findApplicationsByType<T extends TApplication[\"type\"]>(\n type: T,\n ): Extract<TApplication, { type: T }>[];\n findApplicationsByType<T extends TApplication[\"type\"][]>(\n types: T,\n ): Extract<TApplication, { type: T[number] }>[];\n findApplicationsByType(\n _type: TApplication[\"type\"] | TApplication[\"type\"][],\n ): TApplication[] {\n const types = Array.isArray(_type) ? _type : [_type];\n\n return this.applications.filter((r) => types.includes(r.type));\n }\n\n findApplication(\n type: \"media-library\",\n ): Extract<TApplication, { type: \"media-library\" }> | undefined;\n findApplication(\n type: \"canvas\",\n ): Extract<TApplication, { type: \"canvas\" }> | undefined;\n findApplication(\n type: \"coreApp\",\n id: UserApplicationId,\n ): Extract<TApplication, { type: \"coreApp\" }> | undefined;\n findApplication(\n type: \"studio\",\n id: UserApplicationId,\n ): Extract<TApplication, { type: \"studio\" }> | undefined;\n findApplication(\n type: \"workspace\",\n id: string,\n ): Extract<TApplication, { type: \"workspace\" }> | undefined;\n findApplication(\n type: TApplication[\"type\"],\n id?: UserApplicationId | string,\n ): TApplication | undefined {\n switch (type) {\n case \"media-library\":\n case \"canvas\":\n return this.applications.find((r) => r.type === type);\n case \"coreApp\":\n case \"studio\":\n case \"workspace\": {\n return this.applications.find((r) => r.type === type && r.id === id);\n }\n }\n }\n\n toProtocolResources() {\n return this.applications.reduce<ProtocolResource[]>((acc, r) => {\n if (r.type === \"studio\" || r instanceof LocalApplication) return acc;\n return [...acc, r.toProtocolResource()];\n }, []);\n }\n\n static areApplicationsEqual(\n application1?: Application,\n application2?: Application,\n ): boolean {\n if (\n !application1 ||\n !application2 ||\n application1.type !== application2.type\n ) {\n return false;\n }\n\n return application1.id === application2.id;\n }\n}\n","import { z } from \"zod\";\n\n/**\n * Organization ID schema, branded for type safety.\n * @public\n */\nexport const OrganizationId = z.string().nonempty().brand(\"OrganizationId\");\n\n/**\n * Organization ID type, branded for type safety.\n * @public\n */\nexport type OrganizationId = z.output<typeof OrganizationId>;\n\n/**\n * Validates and brands a string as an OrganizationId.\n * @public\n */\nexport function brandOrganizationId(id: string): OrganizationId {\n return OrganizationId.parse(id);\n}\n\nconst OrganizationMember = z.object({\n sanityUserId: z.string(),\n isCurrentUser: z.boolean(),\n user: z.object({\n id: z.string(),\n displayName: z.string(),\n familyName: z.string(),\n givenName: z.string(),\n middleName: z.string().nullable(),\n imageUrl: z.string().nullable(),\n email: z.string(),\n loginProvider: z.string(),\n }),\n roles: z.array(\n z.object({\n name: z.string(),\n title: z.string(),\n description: z.string().optional(),\n }),\n ),\n});\n\n/**\n * @public\n */\nexport type OrganizationMember = z.output<typeof OrganizationMember>;\n\n/**\n * Organization schema — validates and brands API responses\n * from the `/organizations/:id` endpoint.\n * @public\n */\nexport const Organization = z.object({\n id: OrganizationId,\n name: z.string(),\n slug: z.string().nullable(),\n createdAt: z.string(),\n updatedAt: z.string(),\n dashboardStatus: z.enum([\"enabled\", \"disabled\"]),\n aiFeaturesStatus: z.enum([\"enabled\", \"disabled\"]),\n defaultRoleName: z.string(),\n});\n\n/**\n * Represents an organization with optional members and\n * features arrays depending on the generic parameters.\n * - `Organization` — base fields only (default)\n * - `Organization<true>` — includes `members`\n * - `Organization<true, true>` — includes both\n * @public\n */\nexport type Organization<\n IncludeMembers extends boolean = true,\n IncludeFeatures extends boolean = true,\n> = z.output<typeof Organization> &\n (IncludeMembers extends true ? { members: OrganizationMember[] } : unknown) &\n (IncludeFeatures extends true ? { features: string[] } : unknown);\n\n/**\n * Validates and parses a raw API response into a branded\n * Organization. The options control which schema is used —\n * matching what the API returns based on query params.\n * @public\n */\nexport function parseOrganization<\n IncludeMembers extends boolean = true,\n IncludeFeatures extends boolean = true,\n>(\n data: unknown,\n options?: {\n includeMembers?: IncludeMembers;\n includeFeatures?: IncludeFeatures;\n },\n): Organization<IncludeMembers, IncludeFeatures> {\n const includeMembers = options?.includeMembers ?? true;\n const includeFeatures = options?.includeFeatures ?? true;\n\n const extensions = {\n ...(includeMembers && {\n members: z.array(OrganizationMember),\n }),\n ...(includeFeatures && {\n features: z.array(z.string()),\n }),\n };\n\n const schema =\n Object.keys(extensions).length > 0\n ? Organization.extend(extensions)\n : Organization;\n\n return schema.parse(data) as Organization<IncludeMembers, IncludeFeatures>;\n}\n","import type { CanvasResource as ProtocolCanvasResource } from \"@sanity/message-protocol\";\nimport { z } from \"zod\";\n\nimport { AbstractApplication } from \"./applications/application\";\nimport { OrganizationId } from \"./organizations\";\n\n/**\n * Canvas ID schema, branded for type safety.\n * @public\n */\nconst CanvasId = z.string().nonempty().brand(\"CanvasId\");\n\n/**\n * Canvas ID type, branded for type safety.\n * @public\n */\nexport type CanvasId = z.output<typeof CanvasId>;\n\n/**\n * Validates and brands a string as a CanvasId.\n * @public\n */\nexport function brandCanvasId(id: string): CanvasId {\n return CanvasId.parse(id);\n}\n\nconst Canvas = z.object({\n id: CanvasId,\n organizationId: OrganizationId,\n status: z.enum([\"active\", \"provisioning\"]),\n});\n\n/**\n * Represents a Canvas resource as returned from the API.\n * @public\n */\nexport type Canvas = z.output<typeof Canvas>;\n\n/**\n * Validates and parses a raw API response into a branded\n * Canvas.\n * @public\n */\nexport function parseCanvas(data: unknown): Canvas {\n return Canvas.parse(data);\n}\n\n/**\n * Whilst the constructor takes an organization's canvas resource the existance\n * therefore implies the organization has access to the canvas application which\n * is what workbench most importantly wants to know.\n * @public\n */\nexport class CanvasApplication extends AbstractApplication<\"canvas\"> {\n private readonly canvas: Canvas;\n\n constructor(canvas: Canvas) {\n super(\"canvas\");\n\n this.canvas = canvas;\n }\n\n get id(): CanvasId {\n return this.canvas.id;\n }\n\n get href(): string {\n return \"canvas\";\n }\n\n get title(): string {\n return \"Canvas\";\n }\n\n toProtocolResource(): ProtocolCanvasResource {\n return {\n type: \"canvas\",\n id: this.id,\n } satisfies ProtocolCanvasResource;\n }\n}\n","import type { MediaResource as ProtocolMediaResource } from \"@sanity/message-protocol\";\nimport { z } from \"zod\";\n\nimport { AbstractApplication } from \"./applications/application\";\nimport { OrganizationId } from \"./organizations\";\n\n/**\n * Canvas ID schema, branded for type safety.\n * @public\n */\nconst MediaLibraryId = z.string().nonempty().brand(\"MediaLibraryId\");\n\n/**\n * MediaLibrary ID type, branded for type safety.\n * @public\n */\nexport type MediaLibraryId = z.output<typeof MediaLibraryId>;\n\n/**\n * Validates and brands a string as a MediaLibraryId.\n * @public\n */\nexport function brandMediaLibraryId(id: string): MediaLibraryId {\n return MediaLibraryId.parse(id);\n}\n\nconst MediaLibrary = z.object({\n id: MediaLibraryId,\n organizationId: OrganizationId,\n status: z.enum([\"active\", \"provisioning\"]),\n aclMode: z.enum([\"private\", \"public\"]),\n});\n\n/**\n * Represents a MediaLibrary resource as returned from the API.\n * @public\n */\nexport type MediaLibrary = z.output<typeof MediaLibrary>;\n\n/**\n * Validates and parses a raw API response into a branded\n * MediaLibrary.\n * @public\n */\nexport function parseMediaLibrary(data: unknown): MediaLibrary {\n return MediaLibrary.parse(data);\n}\n\n/**\n * Whilst the constructor takes an organization's media library resource the existance\n * therefore implies the organization has access to the media library application which\n * is what workbench most importantly wants to know.\n * @public\n */\n\nexport class MediaLibraryApplication extends AbstractApplication<\"media-library\"> {\n private readonly library: MediaLibrary;\n\n constructor(library: MediaLibrary) {\n super(\"media-library\");\n\n this.library = library;\n }\n\n get id(): string {\n return this.library.id;\n }\n\n get href(): string {\n return \"media\";\n }\n\n get title(): string {\n return \"Media Library\";\n }\n\n toProtocolResource(): ProtocolMediaResource {\n return {\n type: \"media-library\",\n id: this.id,\n } satisfies ProtocolMediaResource;\n }\n}\n","import { z } from \"zod\";\n\nimport { OrganizationId } from \"./organizations\";\n\n/**\n * Project ID schema, branded for type safety.\n * @public\n */\nexport const ProjectId = z.string().nonempty().brand(\"ProjectId\");\n\n/**\n * Project ID type, branded for type safety.\n * @public\n */\nexport type ProjectId = z.output<typeof ProjectId>;\n\n/**\n * Validates and brands a string as a ProjectId.\n * @public\n */\nexport function brandProjectId(id: string): ProjectId {\n return ProjectId.parse(id);\n}\n\nconst ProjectMember = z.object({\n id: z.string(),\n createdAt: z.string(),\n updatedAt: z.string(),\n isCurrentUser: z.boolean(),\n isRobot: z.boolean(),\n roles: z.array(\n z.object({\n name: z.string(),\n title: z.string(),\n description: z.string(),\n }),\n ),\n});\n\n/**\n * @public\n */\nexport type ProjectMember = z.output<typeof ProjectMember>;\n\n/**\n * Project schema — validates and brands API responses\n * from the `/projects/:id` endpoint.\n * @public\n */\nexport const Project = z.object({\n id: ProjectId,\n displayName: z.string(),\n studioHost: z.string().nullable(),\n organizationId: OrganizationId,\n metadata: z.object({\n color: z.string().optional(),\n externalStudioHost: z.string().optional(),\n initialTemplate: z.string().optional(),\n cliInitializedAt: z.string().optional(),\n integration: z.literal([\"manage\", \"cli\"]),\n }),\n isBlocked: z.boolean(),\n isDisabled: z.boolean(),\n isDisabledByUser: z.boolean(),\n activityFeedEnabled: z.boolean(),\n createdAt: z.string(),\n updatedAt: z.string(),\n});\n\n/**\n * Represents a Sanity project with optional members and\n * features arrays depending on the generic parameters.\n * By default, neither members nor features are included.\n * - `Project` — base fields only (default)\n * - `Project<true>` — includes `members`\n * - `Project<true, true>` — includes both\n * @public\n */\nexport type Project<\n IncludeMembers extends boolean = true,\n IncludeFeatures extends boolean = true,\n> = z.output<typeof Project> &\n (IncludeMembers extends true ? { members: ProjectMember[] } : unknown) &\n (IncludeFeatures extends true ? { features: string[] } : unknown);\n\n/**\n * Validates and parses a raw API response into a branded\n * Project. The options control which schema is used —\n * matching what the API returns based on query params.\n * @public\n */\nexport function parseProject<\n IncludeMembers extends boolean = true,\n IncludeFeatures extends boolean = true,\n>(\n data: unknown,\n options?: {\n includeMembers?: IncludeMembers;\n includeFeatures?: IncludeFeatures;\n },\n): Project<IncludeMembers, IncludeFeatures> {\n const includeMembers = options?.includeMembers ?? true;\n const includeFeatures = options?.includeFeatures ?? true;\n\n const extensions = {\n ...(includeMembers && { members: z.array(ProjectMember) }),\n ...(includeFeatures && { features: z.array(z.string()) }),\n };\n\n const schema =\n Object.keys(extensions).length > 0 ? Project.extend(extensions) : Project;\n\n return schema.parse(data) as Project<IncludeMembers, IncludeFeatures>;\n}\n","import type {\n Resource as ProtocolResource,\n UserApplication as UserApplicationData,\n} from \"@sanity/message-protocol\";\nimport { z } from \"zod\";\n\nimport {\n AbstractApplication,\n type AbstractApplicationType,\n} from \"../applications/application\";\n\n/**\n * User application ID schema, branded for type safety.\n * @public\n */\nexport const UserApplicationId = z\n .string()\n .nonempty()\n .brand(\"UserApplicationId\");\n\n/**\n * User application ID type, branded for type safety.\n * @public\n */\nexport type UserApplicationId = z.output<typeof UserApplicationId>;\n\n/**\n * Validates and brands a string as a UserApplicationId.\n * @public\n */\nexport function brandUserApplicationId(id: string): UserApplicationId {\n return UserApplicationId.parse(id);\n}\n\n/**\n * @internal\n */\nexport abstract class UserApplication<\n TUserApplication extends UserApplicationData,\n TType extends Extract<AbstractApplicationType, \"coreApp\" | \"studio\">,\n TProtocolResource extends ProtocolResource = Extract<\n ProtocolResource,\n { type: TType }\n >,\n> extends AbstractApplication<TType, TProtocolResource> {\n readonly application: TUserApplication;\n\n readonly id: UserApplicationId;\n\n constructor(application: TUserApplication, type: TType) {\n super(type);\n this.application = application;\n this.id = brandUserApplicationId(application.id);\n }\n\n abstract get subtitle(): string | undefined;\n\n /**\n * @returns A fully resolved URL instance for the application.\n * For internal applications, constructs a URL using the Sanity studio domain\n * pattern resolved from the environment at the consuming app's build time.\n * For external applications, returns the provided app host as a URL.\n */\n get url(): URL {\n if (this.application.urlType === \"internal\") {\n if (import.meta.env.VITE_SANITY_ENV === \"production\") {\n return new URL(`https://${this.application.appHost}.sanity.studio`);\n }\n return new URL(\n `https://${this.application.appHost}.studio.${import.meta.env.VITE_SANITY_DOMAIN}`,\n );\n }\n\n return new URL(this.application.appHost);\n }\n}\n","import type {\n ApplicationResource as ProtocolApplicationResource,\n CoreApplication,\n} from \"@sanity/message-protocol\";\n\nimport { UserApplication } from \"./user-application\";\n\n/**\n * @internal\n */\nexport class CoreAppApplication extends UserApplication<\n CoreApplication,\n \"coreApp\",\n ProtocolApplicationResource\n> {\n readonly activeDeployment: CoreApplication[\"activeDeployment\"];\n\n constructor(application: CoreApplication) {\n super(application, \"coreApp\");\n\n this.activeDeployment = application.activeDeployment;\n }\n\n get href() {\n return `/application/${this.application.id}`;\n }\n\n get title() {\n return this.activeDeployment?.manifest?.title ?? this.application.title;\n }\n\n get subtitle() {\n return undefined;\n }\n\n get updatedAt() {\n return this.application.updatedAt;\n }\n\n get<TKey extends keyof CoreApplication>(attr: TKey): CoreApplication[TKey] {\n if (!(attr in this.application)) {\n throw new Error(\n `Attribute ${attr.toString()} does not exist on application ${this.application.id}`,\n );\n }\n\n return this.application[attr];\n }\n\n toProtocolResource(): ProtocolApplicationResource {\n return {\n ...this.application,\n type: \"application\",\n url: this.url.toString(),\n } satisfies ProtocolApplicationResource;\n }\n}\n","/**\n * Joins multiple path segments into a single path string.\n * Handles null, undefined, and URL objects gracefully.\n *\n * @param paths - An array of path segments to join.\n * @returns A single joined path string.\n */\nexport const joinUrlPaths = (\n ...paths: Array<string | URL | null | undefined>\n): string => {\n let nextPath = null;\n\n const safeJoinSegments = (\n segment1: string | null | undefined,\n segment2: string | null | undefined,\n ): string => {\n if (!segment1) {\n if (!segment2) {\n return \"\";\n }\n\n return segment2;\n }\n\n if (!segment2) {\n return segment1;\n }\n\n if (segment1.endsWith(\"/\") && segment2.startsWith(\"/\")) {\n return segment1 + segment2.slice(1);\n }\n\n if (segment1.endsWith(\"/\") || segment2.startsWith(\"/\")) {\n return segment1 + segment2;\n }\n\n return `${segment1}/${segment2}`;\n };\n\n const validPaths = paths.filter(\n (path) => path !== null && path !== undefined,\n );\n\n for (const path of validPaths) {\n nextPath = safeJoinSegments(\n nextPath,\n path instanceof URL ? path.pathname : path,\n );\n }\n\n return nextPath ?? \"\";\n};\n\n/**\n * Returns a normalized path by ensuring it starts with a single leading slash\n * and does not end with a trailing slash (unless it's the root path).\n */\nexport function normalizePath(pathname: string): string {\n if (!pathname.startsWith(\"/\")) {\n pathname = `/${pathname}`;\n }\n\n if (pathname !== \"/\" && pathname.endsWith(\"/\")) {\n pathname = pathname.slice(0, -1);\n }\n\n return pathname;\n}\n\n/**\n * The pathname, search, and hash values of a URL.\n */\ninterface Path {\n /**\n * A URL pathname, beginning with a /.\n */\n pathname: string;\n /**\n * A URL search string, beginning with a ?.\n */\n search: string;\n /**\n * A URL fragment identifier, beginning with a #.\n */\n hash: string;\n}\n\n/**\n * Parses a string URL path into its separate pathname, search, and hash components.\n */\nexport function parsePath(path: string): Partial<Path> {\n let parsedPath: Partial<Path> = {};\n\n if (path) {\n let hashIndex = path.indexOf(\"#\");\n if (hashIndex >= 0) {\n parsedPath.hash = path.substring(hashIndex);\n path = path.substring(0, hashIndex);\n }\n\n let searchIndex = path.indexOf(\"?\");\n if (searchIndex >= 0) {\n parsedPath.search = path.substring(searchIndex);\n path = path.substring(0, searchIndex);\n }\n\n if (path) {\n parsedPath.pathname = path;\n }\n }\n\n return parsedPath;\n}\n\n/**\n * Creates a string URL path from the given pathname, search, and hash components.\n */\nexport function createPath({\n pathname = \"/\",\n search = \"\",\n hash = \"\",\n}: Partial<Path>) {\n if (search && search !== \"?\")\n pathname += search.charAt(0) === \"?\" ? search : `?${search}`;\n if (hash && hash !== \"#\")\n pathname += hash.charAt(0) === \"#\" ? hash : `#${hash}`;\n return pathname;\n}\n","import type {\n StudioApplication as StudioApplicationData,\n // eslint-disable-next-line no-restricted-imports\n StudioResource as ProtocolStudioResource,\n WorkspaceManifest,\n} from \"@sanity/message-protocol\";\nimport type { SemVer } from \"semver\";\nimport { coerce, gt, gte, lt, rsort, valid } from \"semver\";\nimport { z } from \"zod\";\n\nimport { AbstractApplication } from \"../applications/application\";\nimport {\n brandProjectId,\n ProjectId as ProjectIdSchema,\n type Project,\n type ProjectId,\n} from \"../projects\";\nimport { joinUrlPaths, normalizePath } from \"../shared/urls\";\nimport { type UserApplicationId } from \"./user-application\";\nimport { UserApplication } from \"./user-application\";\n\nconst WORKSPACE_MANIFEST_SCHEMA = z.object({\n name: z.string(),\n title: z.string(),\n subtitle: z.string().nullable().optional(),\n basePath: z.string(),\n projectId: ProjectIdSchema,\n dataset: z.string(),\n icon: z.string().optional().nullable(),\n schema: z.string(),\n tools: z.string().optional(),\n});\n\nconst STUDIO_MANIFEST_SCHEMA = z\n .object({\n // data from manifest\n version: z.number(),\n createdAt: z.string(),\n studioVersion: z.string().nullable().optional(),\n // data from workspace object\n workspaces: z.array(WORKSPACE_MANIFEST_SCHEMA),\n })\n .superRefine((data, ctx) => {\n if (!data.version) {\n ctx.addIssue({\n code: \"invalid_type\",\n message: \"Manifest version is too old\",\n expected: \"number\",\n });\n }\n\n if (data.version < 2) {\n ctx.addIssue({\n code: \"invalid_value\",\n message: \"Manifest version is too old\",\n values: [2, 3],\n });\n }\n\n if (data.version >= 3 && !data.studioVersion) {\n ctx.addIssue({\n code: \"invalid_type\",\n message: \"Manifest version 3 or higher requires a `studioVersion`\",\n expected: \"string\",\n });\n }\n });\n\nconst DEFAULT_WORKSPACE_DATA = {\n name: \"default\",\n title: \"Default\",\n basePath: \"/\",\n} as const satisfies Partial<Workspace>;\n\n/**\n * @internal\n */\nexport class StudioApplication extends UserApplication<\n StudioApplicationData,\n \"studio\",\n never\n> {\n /**\n * Returns a list of studio workspaces based on the application manifest.\n * If there is no manifest, or alternatively it is not valid, then we create a default workspace.\n */\n readonly workspaces: readonly StudioWorkspace[] = [];\n\n readonly project: Project<false, false>;\n\n /**\n * @param application - The studio application to create a list of workspaces for\n * @param projects - The projects available in the organization. It's not enough to just pass\n * the project that associates with the application because that is the project the app is deployed in relation to.\n * The workspaces may have different projects completely.\n */\n constructor(\n application: StudioApplicationData,\n projects: Project<false, false>[],\n ) {\n super(application, \"studio\");\n\n /**\n * If the application has a manifest, then we validate it and create\n * a workspace for each manifest entry. Otherwise, we will create a \"default\"\n * workspace which is pretty much identical to when a user has a single workspace\n * studio application using the term \"default\" in places.\n */\n let workspaces: Workspace[] = [];\n if (this.hasManifest) {\n /**\n * If the workspaces fail to parse we dont want to throw an error because\n * this could potentially crash the app. Instead we log a warning and return an empty array.\n */\n try {\n workspaces = STUDIO_MANIFEST_SCHEMA.parse(\n application.manifestData?.value,\n ).workspaces;\n } catch (error) {\n console.warn(\n `Failed to parse manifest for application ${application.id}`,\n error,\n );\n }\n }\n\n if (workspaces.length === 0) {\n /**\n * If the user application does not have a valid manifest or no workspaces,\n * we attempt to get the manifest from the server-side maninfest first and then\n * fall back to the live-manifest.\n *\n * In the future this should happen even before checking the traditional manifest,\n * but for now we keep the current order to not introduce breaking changes.\n *\n * The live manifest is inherintly less reliable as it depends on which user has\n * uploaded it, which is why this is wrapped in a feature flag for now.\n */\n const deploymentManifest = application.activeDeployment?.manifest;\n // const liveManifest = growthbook.isOn('dashboard-use-live-manifest')\n // ? application.config['live-manifest']?.value\n // : null\n\n const serverManifest = deploymentManifest; /*?? liveManifest*/\n const serverManifestWorkspaces = serverManifest?.workspaces;\n\n if (\n Array.isArray(serverManifestWorkspaces) &&\n serverManifestWorkspaces.length\n ) {\n workspaces = serverManifestWorkspaces.map((w) => ({\n ...w,\n projectId: brandProjectId(w.projectId),\n }));\n }\n }\n\n /**\n * Filter all the workspaces that have a project the user does not have access to\n */\n const workspacesWithProjectsMap = workspaces.reduce((acc, workspace) => {\n const project = projects.find((p) => p.id === workspace.projectId);\n\n if (project) {\n acc.set(workspace, project);\n } else {\n console.warn(\n `Project not found for application ${application.id} and workspace ${workspace.name}. This workspace has been omitted.`,\n );\n }\n\n return acc;\n }, new Map<Workspace, Project<false, false>>());\n\n const brandedProjectId = brandProjectId(application.projectId);\n const project = projects.find((p) => p.id === brandedProjectId);\n\n if (!project) {\n throw new Error(`Project not found for application ${application.id}`);\n }\n\n if (workspacesWithProjectsMap.size === 0) {\n /**\n * If there are still no workspaces, we create a default workspace.\n */\n workspacesWithProjectsMap.set(\n {\n ...DEFAULT_WORKSPACE_DATA,\n projectId: brandedProjectId,\n } satisfies Workspace,\n project,\n );\n }\n\n this.workspaces = Object.freeze(\n Array.from(workspacesWithProjectsMap.entries()).map(([workspace, p]) => {\n /**\n * A workspace is considered the default if the dashboard generated it OR because\n * the properties match that of the default workspace generated by the studio.\n * Which is why these values will match because dashboard generates an identical\n * workspace to the default studio one.\n */\n const isDefaultWorkspace =\n workspace.name === DEFAULT_WORKSPACE_DATA.name &&\n workspace.basePath === DEFAULT_WORKSPACE_DATA.basePath &&\n workspace.title === DEFAULT_WORKSPACE_DATA.title;\n\n return new StudioWorkspace(this, workspace, p, isDefaultWorkspace);\n }),\n );\n\n this.project = project;\n }\n\n get href() {\n return `/studio/${this.application.id}`;\n }\n\n get title() {\n // Get the title from the user application, this has the highest precedence\n const title = this.get(\"title\");\n if (title) {\n return title;\n }\n\n // If there are multiple workspaces and the studio is internal, use the project display name\n if (this.workspaces.length > 1 && this.get(\"urlType\") === \"internal\") {\n return this.project.displayName;\n }\n\n // Otherwise use the title of the first workspace\n return this.workspaces[0].title;\n }\n\n get subtitle() {\n return new URL(this.url).hostname;\n }\n\n get<TKey extends keyof StudioApplicationData>(\n attr: TKey,\n ): StudioApplicationData[TKey] {\n if (!(attr in this.application)) {\n throw new Error(\n `Attribute ${attr.toString()} does not exist on studio ${this.application.id}`,\n );\n }\n\n return this.application[attr];\n }\n\n get hasManifest(): boolean {\n return (\n typeof this.application.manifestData?.value?.version === \"number\" &&\n this.application.manifestData?.value?.version >= 2\n );\n }\n\n get hasSchema(): boolean {\n // In this case we can not look at `this.workspaces` because it won't contain workspaces the user does\n // not have access to. The application has a schema, even if the user can't access any of the workspaces,\n // otherwise it would be displayed as not having a manifest in the setup guide and therefore considered\n // partially compatible.\n const workspaces = this.get(\"manifestData\")?.value?.workspaces ?? [];\n\n if (workspaces.length === 0) {\n return false;\n }\n\n return workspaces.every((w) => Boolean(w.schema));\n }\n\n private resolveVersion() {\n // const growthbook = getGrowthbook()\n\n let version: string | undefined;\n\n if (this.get(\"urlType\") === \"internal\") {\n version = this.get(\"activeDeployment\")?.version;\n } else {\n const manifest = this.get(\"manifestData\")?.value;\n // The property 'studioVersion' exists only on external studios,\n // starting from manifest.version 3\n version =\n manifest && \"studioVersion\" in manifest\n ? manifest.studioVersion\n : undefined;\n }\n\n /**\n * Self-hosted studios are often not publicly accessible, which means that\n * Brett often times cannot fetch the manifest to read the version. In this case\n * we try to read the version from the deployment manifest or 'live-manifest', if it exists, to do\n * everything we can to determine the version.\n *\n * Since the data of the live-maninfest can not be trusted, we also must validate\n * that the version is a valid semver version before returning it. It always represents\n * the last known version from when the studio was connected to Sanity's infrastructure,\n * which might not be the version this studio is currently running (e.g. because the version\n * has been downgraded).\n */\n const deploymentManifest = this.get(\"activeDeployment\")?.manifest;\n // const liveManifest = growthbook.isOn('dashboard-use-live-manifest')\n // ? this.get('config')?.['live-manifest']?.value\n // : null\n\n const serverManifest = deploymentManifest; /*?? liveManifest*/\n const bundleVersion = serverManifest?.bundleVersion;\n\n if (bundleVersion && valid(coerce(bundleVersion))) {\n if (!version || (version && gt(bundleVersion, version))) {\n version = bundleVersion;\n }\n }\n\n if (!version || !valid(coerce(version))) {\n return null;\n }\n\n return coerce(version);\n }\n\n get version() {\n const version = this.resolveVersion();\n return version ? version.toString() : null;\n }\n\n get compatibilityStatus(): CompatibilityStatus {\n return StudioApplication.resolveCompatibilityStatus(this);\n }\n\n /**\n * Used to calculate the compatibility status of a given studio application.\n * Optionally if you've resolved the version elsewhere provide that value to\n * get the new compatibility status without mutating the application.\n */\n static resolveCompatibilityStatus(\n application: StudioApplication,\n version: string | null = application.version,\n ): CompatibilityStatus {\n if (\n version === null ||\n lt(version, StudioApplication.MinimumStudioVersion)\n ) {\n return StudioApplication.CompatibilityStatuses.UNKNOWN;\n }\n\n if (\n !application.hasSchema ||\n !application.hasManifest ||\n StudioApplication.resolveIssues(application, version).length > 0\n ) {\n return StudioApplication.CompatibilityStatuses.PARTIALLY_COMPATIBLE;\n }\n\n return StudioApplication.CompatibilityStatuses.FULLY_COMPATIBLE;\n }\n\n get isAutoRedirecting(): boolean {\n return StudioApplication.resolveIsAutoRedirecting(this);\n }\n\n /**\n * Mirrors the `isRedirectable` function from Saison defined in\n * https://github.com/sanity-io/saison/blob/83556405d23e07f6d3a71c76249c67e33fe1101f/src/utils/applications.ts\n *\n * Returns whether a studio application is auto-redirecting, meaning it can only be accessed in the context of\n * Dashboard.\n */\n static resolveIsAutoRedirecting(\n application: StudioApplication,\n versionArg = application.version,\n ) {\n let version: string | SemVer | null = versionArg;\n\n if (application.get(\"urlType\") === \"external\" || !version) {\n return false;\n }\n\n if (!application.get(\"activeDeployment\")?.isAutoUpdating) {\n // If the studio is not auto-updating, we need to check if the version supports\n // workspace switcher (3.92.0+) otherwise it would not be redirected.\n return gt(version, \"3.92.0\");\n }\n\n const autoUpdatingVersion = application.get(\"autoUpdatingVersion\");\n\n if (autoUpdatingVersion) {\n if ([\"next\", \"stable\", \"latest\"].includes(autoUpdatingVersion)) {\n return true;\n }\n\n const autoUpdatingVersionPinnedVersion = coerce(autoUpdatingVersion);\n\n if (autoUpdatingVersionPinnedVersion) {\n version = autoUpdatingVersionPinnedVersion;\n }\n }\n\n return gt(version, StudioApplication.MinimumStudioVersion);\n }\n\n /**\n * Returns a list of issues that prevent the studio from functioning properly in the Dashboard.\n * This static value depends on the version of the studio that comes from the `activeDeployment` property.\n * As such, if the studio is auto-updating, this list will be incorrect & instead you should use\n * the static method `resolveIssues` to get the correct issues by passing the resolved version.\n */\n get issues(): StudioIssues {\n return StudioApplication.resolveIssues(this);\n }\n\n static resolveIssues(\n application: StudioApplication,\n version: string | null = application.version,\n ): StudioIssues {\n const issues: StudioIssues = StudioApplication.Features.filter(\n (feature) => {\n return !application.isFeatureSupported(feature.id, version);\n },\n );\n\n if (!application.hasManifest) {\n issues.push({\n id: StudioApplication.StudioIssues.ISSUE_MANIFEST,\n });\n }\n\n return issues;\n }\n\n protected isFeatureSupported(\n feature: StudioDashboardIssue,\n version = this.version,\n ) {\n const featureVersion = StudioApplication.Features.find(\n (_) => _.id === feature,\n )?.version;\n\n if (!featureVersion || !version) {\n return false;\n }\n\n return gte(version, featureVersion);\n }\n\n toProtocolResource(): never {\n throw new Error(\n \"Studio application resources cannot be converted to protocol resources\",\n );\n }\n\n static CompatibilityStatuses = {\n UNKNOWN: \"unknown\",\n PARTIALLY_COMPATIBLE: \"partially-compatible\",\n FULLY_COMPATIBLE: \"fully-compatible\",\n } as const;\n\n static StudioIssues = {\n ISSUE_ACTIVITY: \"ACTIVITY\",\n ISSUE_AGENT: \"AGENT\",\n ISSUE_FAVORITES: \"FAVORITES\",\n ISSUE_URL_SYNCING: \"URL_SYNCING\",\n ISSUE_UI_ADJUSTMENT: \"UI_ADJUSTMENT\",\n ISSUE_CONTENT_MAPPING: \"CONTENT_MAPPING\",\n ISSUE_LOGIN: \"LOGIN\",\n ISSUE_MANIFEST: \"MANIFEST\",\n } as const;\n\n static Features = [\n {\n id: StudioApplication.StudioIssues.ISSUE_AGENT,\n version: \"5.1.0\",\n },\n {\n id: StudioApplication.StudioIssues.ISSUE_FAVORITES,\n version: \"3.88.1\",\n },\n {\n id: StudioApplication.StudioIssues.ISSUE_ACTIVITY,\n version: \"3.88.1\",\n },\n {\n id: StudioApplication.StudioIssues.ISSUE_URL_SYNCING,\n version: \"3.75.0\",\n },\n {\n id: StudioApplication.StudioIssues.ISSUE_UI_ADJUSTMENT,\n version: \"3.78.1\",\n },\n {\n id: StudioApplication.StudioIssues.ISSUE_CONTENT_MAPPING,\n version: \"3.68.0\",\n },\n {\n id: StudioApplication.StudioIssues.ISSUE_LOGIN,\n version: \"2.28.0\",\n },\n ] satisfies StudioIssues;\n\n static MinimumStudioVersion = \"2.28.0\" as const;\n\n static MinimumStudioVersionWithNoIssues = rsort(\n StudioApplication.Features.map((feature) => feature.version),\n ).at(0);\n}\n\ntype CompatibilityStatus =\n (typeof StudioApplication.CompatibilityStatuses)[keyof typeof StudioApplication.CompatibilityStatuses];\n\ntype StudioDashboardIssue =\n (typeof StudioApplication.StudioIssues)[keyof typeof StudioApplication.StudioIssues];\n\ntype StudioIssues = Array<{\n id: StudioDashboardIssue;\n version?: string;\n}>;\n\ntype Workspace = Omit<WorkspaceManifest, \"dataset\" | \"schema\" | \"projectId\"> & {\n projectId: ProjectId;\n dataset?: string;\n schema?: string;\n};\n\n/**\n * @internal\n */\nexport class StudioWorkspace extends AbstractApplication<\n \"workspace\",\n ProtocolStudioResource\n> {\n /**\n * Workspaces always belong to a studio application.\n * They do not exist on their own & therefore can access\n * information about the studio they're in via this property.\n */\n private readonly studioApplication: StudioApplication;\n private readonly workspace: Workspace;\n readonly project: Project<false, false>;\n private readonly isDefaultWorkspace: boolean;\n\n constructor(\n studioApplication: StudioApplication,\n workspace: Workspace,\n project: Project<false, false>,\n isDefaultWorkspace: boolean,\n ) {\n super(\"workspace\");\n\n this.studioApplication = studioApplication;\n this.workspace = workspace;\n this.project = project;\n this.isDefaultWorkspace = isDefaultWorkspace;\n }\n\n /**\n * The studio application that this workspace belongs to.\n */\n get studio() {\n return this.studioApplication;\n }\n\n get id() {\n return StudioWorkspace.makeId(this.studio.id, this.workspace.name);\n }\n\n get href() {\n return joinUrlPaths(this.studio.href, this.workspace.name);\n }\n\n get title() {\n /**\n * If there's no manifest we will have created a single workspace for the application.\n * In this circumstance we won't have a meaningful title, so instead we use the hostname of the appHost.\n */\n if (this.isDefaultWorkspace) {\n return this.project.displayName;\n }\n\n return this.workspace.title;\n }\n\n get subtitle() {\n if (this.isDefaultWorkspace) {\n const isValidAppHost = URL.canParse(this.studio.get(\"appHost\"));\n const url = isValidAppHost\n ? new URL(this.studio.get(\"appHost\"))\n : this.studio.url;\n\n return url.hostname;\n }\n\n return this.get(\"subtitle\");\n }\n\n get<TKey extends Exclude<keyof Workspace, \"id\" | \"icon\" | \"title\">>(\n attr: TKey,\n ): Workspace[TKey] {\n return this.workspace[attr];\n }\n\n /**\n * With comlink, studio-applications were not considered applications at all, only workspaces. This is partially why\n * we create default workspaces when the application has no manifest or the manifest has no workspaces.\n *\n * Thereby, to create it we depend on a lot of information from the parent application even if it's duplicated across\n * different workspaces.\n */\n toProtocolResource(): ProtocolStudioResource {\n return {\n ...this.workspace,\n type: \"studio\",\n userApplicationId: this.studio.id,\n activeDeployment: this.studio.get(\"activeDeployment\"),\n autoUpdatingVersion: this.studio.get(\"autoUpdatingVersion\"),\n dashboardStatus: this.studio.get(\"dashboardStatus\"),\n url: this.studio.url.toString(),\n href: this.href,\n id: this.id,\n hasManifest: true,\n hasSchema: Boolean(this.workspace.schema),\n manifest: this.studio.get(\"manifestData\")?.value ?? null,\n updatedAt: this.studio.get(\"updatedAt\"),\n version: this.studio.get(\"activeDeployment\")?.version,\n urlType: this.studio.get(\"urlType\"),\n config: this.studio.get(\"config\"),\n } satisfies ProtocolStudioResource;\n }\n\n /**\n * @returns the URL to the workspace of a studio application.\n */\n get url(): URL {\n const studioUrl = new URL(this.studio.url);\n const normalizedUrlPath = normalizePath(studioUrl.pathname);\n\n let finalBasePath = normalizePath(this.get(\"basePath\"));\n\n // the appHost may already contain the basepath for externally hosted studios\n // or embedded studios, so we deduplicate the segments\n if (finalBasePath.startsWith(normalizedUrlPath)) {\n finalBasePath = finalBasePath.slice(normalizedUrlPath.length);\n }\n\n studioUrl.pathname = joinUrlPaths(normalizedUrlPath, finalBasePath);\n\n return studioUrl;\n }\n\n static makeId(applicationId: UserApplicationId, workspaceName: string) {\n return `${applicationId}-${workspaceName}`;\n }\n\n static splitId(id: string): [string, string] {\n return id.split(new RegExp(/-(.*)/)).slice(0, 2) as [string, string];\n }\n}\n"],"names":["ProjectIdSchema","project"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workbench",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.7",
|
|
4
4
|
"description": "Workbench component for the Sanity Content Operating System",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/sanity-io/workbench/packages/@sanity/workbench#readme",
|
|
@@ -24,18 +24,24 @@
|
|
|
24
24
|
"source": "./src/_exports/_internal.ts",
|
|
25
25
|
"default": "./dist/_internal.js"
|
|
26
26
|
},
|
|
27
|
-
"./
|
|
28
|
-
"source": "./src/_exports/
|
|
29
|
-
"default": "./dist/
|
|
27
|
+
"./core": {
|
|
28
|
+
"source": "./src/_exports/core.ts",
|
|
29
|
+
"default": "./dist/core.js"
|
|
30
30
|
},
|
|
31
31
|
"./package.json": "./package.json"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@sanity/message-protocol": "^0.23.0",
|
|
35
|
+
"rxjs": "^7.8.2",
|
|
36
|
+
"semver": "^7.7.4",
|
|
37
|
+
"zod": "^4.3.6",
|
|
34
38
|
"@sanity/federation": "0.1.0-alpha.5"
|
|
35
39
|
},
|
|
36
40
|
"devDependencies": {
|
|
37
41
|
"@sanity/pkg-utils": "^9.2.3",
|
|
42
|
+
"@types/semver": "^7.7.1",
|
|
38
43
|
"typescript": "^6.0.2",
|
|
44
|
+
"vite": "^7.3.1",
|
|
39
45
|
"@repo/oxc-config": "0.0.0",
|
|
40
46
|
"@repo/tsconfig": "0.0.1"
|
|
41
47
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../core";
|
package/src/_internal/index.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
3
|
|
|
3
4
|
import { renderWorkbench } from "./render";
|
|
4
5
|
|
|
6
|
+
const mockLoadRemote = vi.fn();
|
|
7
|
+
|
|
8
|
+
vi.mock("@sanity/federation/runtime", () => ({
|
|
9
|
+
createInstance: () => ({ loadRemote: mockLoadRemote }),
|
|
10
|
+
}));
|
|
11
|
+
|
|
5
12
|
describe("renderWorkbench", () => {
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
vi.unstubAllEnvs();
|
|
15
|
+
vi.restoreAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
6
18
|
it("throws when rootElement is missing", async () => {
|
|
7
19
|
await expect(
|
|
8
20
|
renderWorkbench(null as unknown as HTMLElement),
|
|
@@ -15,4 +27,47 @@ describe("renderWorkbench", () => {
|
|
|
15
27
|
"SANITY_INTERNAL_WORKBENCH_REMOTE_URL is not set",
|
|
16
28
|
);
|
|
17
29
|
});
|
|
30
|
+
|
|
31
|
+
it("throws when remote module is null", async () => {
|
|
32
|
+
vi.stubEnv("SANITY_INTERNAL_WORKBENCH_REMOTE_URL", "http://localhost:3001");
|
|
33
|
+
mockLoadRemote.mockResolvedValue(null);
|
|
34
|
+
|
|
35
|
+
const el = document.createElement("div");
|
|
36
|
+
await expect(renderWorkbench(el)).rejects.toThrow(
|
|
37
|
+
'Remote module "workbench-remote/App" did not expose a render function',
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("throws when remote module does not expose a render function", async () => {
|
|
42
|
+
vi.stubEnv("SANITY_INTERNAL_WORKBENCH_REMOTE_URL", "http://localhost:3001");
|
|
43
|
+
mockLoadRemote.mockResolvedValue({ notRender: true });
|
|
44
|
+
|
|
45
|
+
const el = document.createElement("div");
|
|
46
|
+
await expect(renderWorkbench(el)).rejects.toThrow(
|
|
47
|
+
'Remote module "workbench-remote/App" did not expose a render function',
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("calls render on the remote module and returns the cleanup function", async () => {
|
|
52
|
+
vi.stubEnv("SANITY_INTERNAL_WORKBENCH_REMOTE_URL", "http://localhost:3001");
|
|
53
|
+
const cleanup = vi.fn();
|
|
54
|
+
const render = vi.fn().mockReturnValue(cleanup);
|
|
55
|
+
mockLoadRemote.mockResolvedValue({ render });
|
|
56
|
+
|
|
57
|
+
const el = document.createElement("div");
|
|
58
|
+
const config = {};
|
|
59
|
+
const options = { reactStrictMode: true };
|
|
60
|
+
|
|
61
|
+
const result = await renderWorkbench(el, config, options);
|
|
62
|
+
|
|
63
|
+
expect(render).toHaveBeenCalledWith(
|
|
64
|
+
el,
|
|
65
|
+
{ config, localApplications: expect.any(BehaviorSubject) },
|
|
66
|
+
options,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(result).toBeTypeOf("function");
|
|
70
|
+
result();
|
|
71
|
+
expect(cleanup).toHaveBeenCalled();
|
|
72
|
+
});
|
|
18
73
|
});
|
package/src/_internal/render.ts
CHANGED
|
@@ -1,32 +1,24 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
|
|
1
3
|
import { createInstance } from "@sanity/federation/runtime";
|
|
2
4
|
import { log } from "@sanity/federation/runtime/plugins/log";
|
|
3
|
-
import {
|
|
5
|
+
import { BehaviorSubject, type Observable } from "rxjs";
|
|
6
|
+
|
|
7
|
+
import type { LocalApplicationData } from "../core/applications/local-application";
|
|
8
|
+
import type { Config, RemoteModuleRenderOptions } from "../core/config";
|
|
9
|
+
import { createLogger } from "../core/log";
|
|
4
10
|
|
|
5
11
|
const logger = createLogger({
|
|
6
12
|
namespace: "sanity-workbench",
|
|
7
13
|
logLevel: "debug",
|
|
8
14
|
});
|
|
9
15
|
|
|
10
|
-
/**
|
|
11
|
-
* Workbench configuration.
|
|
12
|
-
*
|
|
13
|
-
* @public
|
|
14
|
-
*/
|
|
15
|
-
export type Config = {
|
|
16
|
-
/**
|
|
17
|
-
* The organization ID to use when rendering the workbench.
|
|
18
|
-
*/
|
|
19
|
-
organizationId: string | undefined;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
16
|
/**
|
|
23
17
|
* Options for rendering the workbench.
|
|
24
18
|
*
|
|
25
19
|
* @public
|
|
26
20
|
*/
|
|
27
|
-
export interface RenderWorkbenchOptions {
|
|
28
|
-
reactStrictMode?: boolean;
|
|
29
|
-
}
|
|
21
|
+
export interface RenderWorkbenchOptions extends RemoteModuleRenderOptions {}
|
|
30
22
|
|
|
31
23
|
declare global {
|
|
32
24
|
interface ImportMetaEnv {
|
|
@@ -37,22 +29,30 @@ declare global {
|
|
|
37
29
|
}
|
|
38
30
|
}
|
|
39
31
|
|
|
32
|
+
type RemoteModule<TProps extends any> = {
|
|
33
|
+
render: (
|
|
34
|
+
rootElement: HTMLElement,
|
|
35
|
+
props: TProps,
|
|
36
|
+
options?: RenderWorkbenchOptions,
|
|
37
|
+
) => () => void;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
40
|
/**
|
|
41
41
|
* Module defining the remote workbench application.
|
|
42
42
|
*
|
|
43
43
|
* @internal
|
|
44
44
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
options?: RenderWorkbenchOptions,
|
|
50
|
-
) => () => void;
|
|
51
|
-
}
|
|
45
|
+
type WorkbenchRemoteModule = RemoteModule<{
|
|
46
|
+
config?: Config;
|
|
47
|
+
localApplications?: Observable<LocalApplicationData[]>;
|
|
48
|
+
}>;
|
|
52
49
|
|
|
53
50
|
const REMOTE_NAME = "workbench-remote";
|
|
54
51
|
const REMOTE_MODULE = "App";
|
|
55
52
|
|
|
53
|
+
const LOCAL_APPS_HMR_EVENT = "sanity:workbench:local-applications";
|
|
54
|
+
const LOCAL_APPS_HMR_REQUEST = "sanity:workbench:get-local-applications";
|
|
55
|
+
|
|
56
56
|
/**
|
|
57
57
|
* Creates a Module Federation instance, loads a remote workbench
|
|
58
58
|
* application, and renders it into the provided root element.
|
|
@@ -101,5 +101,32 @@ export async function renderWorkbench(
|
|
|
101
101
|
);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
let localApplications = undefined;
|
|
105
|
+
let cleanupHmr = () => {};
|
|
106
|
+
|
|
107
|
+
if (import.meta.hot) {
|
|
108
|
+
const localApps$ = new BehaviorSubject<LocalApplicationData[]>([]);
|
|
109
|
+
|
|
110
|
+
const handler = (payload: { applications: LocalApplicationData[] }) => {
|
|
111
|
+
localApps$.next(payload.applications);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
import.meta.hot.on(LOCAL_APPS_HMR_EVENT, handler);
|
|
115
|
+
import.meta.hot.send(LOCAL_APPS_HMR_REQUEST);
|
|
116
|
+
|
|
117
|
+
localApplications = localApps$;
|
|
118
|
+
|
|
119
|
+
cleanupHmr = () => import.meta.hot?.off(LOCAL_APPS_HMR_EVENT, handler);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const unmount = remoteModule.render(
|
|
123
|
+
rootElement,
|
|
124
|
+
{ config, localApplications },
|
|
125
|
+
options,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return () => {
|
|
129
|
+
cleanupHmr();
|
|
130
|
+
unmount();
|
|
131
|
+
};
|
|
105
132
|
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CoreApplication,
|
|
3
|
+
StudioApplication as StudioApplicationApiResponse,
|
|
4
|
+
} from "@sanity/message-protocol";
|
|
5
|
+
|
|
6
|
+
import { parseCanvas } from "../canvases";
|
|
7
|
+
import { parseMediaLibrary } from "../media-libraries";
|
|
8
|
+
import { parseProject } from "../projects";
|
|
9
|
+
|
|
10
|
+
export const CANVAS_DATA = parseCanvas({
|
|
11
|
+
organizationId: "oSyH1iET5",
|
|
12
|
+
id: "cac1Na6lwtEI",
|
|
13
|
+
status: "active",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const MEDIA_LIBRARY_DATA = parseMediaLibrary({
|
|
17
|
+
organizationId: "oSyH1iET5",
|
|
18
|
+
id: "al34RQcBfuD7",
|
|
19
|
+
status: "active",
|
|
20
|
+
aclMode: "private",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const PROJECTS = [
|
|
24
|
+
parseProject(
|
|
25
|
+
{
|
|
26
|
+
id: "c16r74n4",
|
|
27
|
+
displayName: "Sanity Home",
|
|
28
|
+
studioHost: "sanity-home",
|
|
29
|
+
isBlocked: false,
|
|
30
|
+
isDisabled: false,
|
|
31
|
+
isDisabledByUser: false,
|
|
32
|
+
metadata: {
|
|
33
|
+
integration: "cli",
|
|
34
|
+
color: "a",
|
|
35
|
+
initialTemplate: "cli-clean",
|
|
36
|
+
externalStudioHost: "https://home.sanity.team/admin",
|
|
37
|
+
cliInitializedAt: "2024-03-13T10:16:07.297Z",
|
|
38
|
+
},
|
|
39
|
+
activityFeedEnabled: true,
|
|
40
|
+
createdAt: "2024-03-13T10:16:07.297Z",
|
|
41
|
+
updatedAt: "2025-07-08T20:13:22.574Z",
|
|
42
|
+
organizationId: "oSyH1iET5",
|
|
43
|
+
features: [],
|
|
44
|
+
},
|
|
45
|
+
{ includeMembers: false, includeFeatures: true },
|
|
46
|
+
),
|
|
47
|
+
parseProject(
|
|
48
|
+
{
|
|
49
|
+
id: "hzao7xsp",
|
|
50
|
+
displayName: "Customer 360",
|
|
51
|
+
studioHost: null,
|
|
52
|
+
isBlocked: false,
|
|
53
|
+
isDisabled: false,
|
|
54
|
+
isDisabledByUser: false,
|
|
55
|
+
metadata: {
|
|
56
|
+
integration: "cli",
|
|
57
|
+
color: "b",
|
|
58
|
+
initialTemplate: "cli-clean",
|
|
59
|
+
externalStudioHost: "https://360.sanity.build",
|
|
60
|
+
cliInitializedAt: "2024-01-06T21:09:20.688Z",
|
|
61
|
+
},
|
|
62
|
+
activityFeedEnabled: true,
|
|
63
|
+
createdAt: "2024-01-06T21:09:20.688Z",
|
|
64
|
+
updatedAt: "2025-07-04T17:47:55.830Z",
|
|
65
|
+
organizationId: "oSyH1iET5",
|
|
66
|
+
features: [],
|
|
67
|
+
},
|
|
68
|
+
{ includeMembers: false, includeFeatures: true },
|
|
69
|
+
),
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
export const APPLICATION_DATA = {
|
|
73
|
+
id: "v27rvqtlp3lmdvcln6ey3lro",
|
|
74
|
+
projectId: null,
|
|
75
|
+
organizationId: "oSyH1iET5",
|
|
76
|
+
title: "sdk-movie-list",
|
|
77
|
+
type: "coreApp",
|
|
78
|
+
urlType: "internal",
|
|
79
|
+
appHost: "x7apsmr6fxvc",
|
|
80
|
+
dashboardStatus: "default",
|
|
81
|
+
createdAt: "2025-03-27T19:00:32.792Z",
|
|
82
|
+
updatedAt: "2025-03-27T19:00:32.792Z",
|
|
83
|
+
activeDeployment: {
|
|
84
|
+
id: "dv3kz3fsl4aqha3parc8k391",
|
|
85
|
+
version: "3.81.0",
|
|
86
|
+
isActiveDeployment: true,
|
|
87
|
+
userApplicationId: "v27rvqtlp3lmdvcln6ey3lro",
|
|
88
|
+
isAutoUpdating: false,
|
|
89
|
+
size: 528292,
|
|
90
|
+
deployedAt: "2025-03-27T19:07:21.082Z",
|
|
91
|
+
deployedBy: "gwXueEBci",
|
|
92
|
+
createdAt: "2025-03-27T19:07:21.038Z",
|
|
93
|
+
updatedAt: "2025-03-27T19:07:21.082Z",
|
|
94
|
+
manifest: null,
|
|
95
|
+
},
|
|
96
|
+
} satisfies CoreApplication;
|
|
97
|
+
|
|
98
|
+
export const MULTIPLE_WORKSPACE_STUDIO_DATA = {
|
|
99
|
+
id: "75affa24c862a372764ceb40",
|
|
100
|
+
projectId: "hzao7xsp",
|
|
101
|
+
organizationId: null,
|
|
102
|
+
title: null,
|
|
103
|
+
type: "studio",
|
|
104
|
+
urlType: "external",
|
|
105
|
+
appHost: "https://360.sanity.build",
|
|
106
|
+
dashboardStatus: "default",
|
|
107
|
+
createdAt: "2024-08-09T14:04:57.477Z",
|
|
108
|
+
updatedAt: "2025-05-06T20:12:05.955Z",
|
|
109
|
+
autoUpdatingVersion: null,
|
|
110
|
+
activeDeployment: null,
|
|
111
|
+
manifest: null,
|
|
112
|
+
manifestData: {
|
|
113
|
+
value: {
|
|
114
|
+
version: 2,
|
|
115
|
+
createdAt: "2025-11-19T07:59:25.943Z",
|
|
116
|
+
workspaces: [
|
|
117
|
+
{
|
|
118
|
+
name: "explorer",
|
|
119
|
+
title: "Customer 360",
|
|
120
|
+
basePath: "/360",
|
|
121
|
+
projectId: "hzao7xsp",
|
|
122
|
+
dataset: "production",
|
|
123
|
+
icon: '<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="50" height="50" fill="#FF5500"></rect><path d="M8.5 25C8.5 27.1668 8.92678 29.3124 9.75599 31.3143C10.5852 33.3161 11.8006 35.1351 13.3327 36.6673C14.8649 38.1994 16.6839 39.4148 18.6857 40.244C20.6876 41.0732 22.8332 41.5 25 41.5C27.1668 41.5 29.3124 41.0732 31.3143 40.244C33.3161 39.4148 35.1351 38.1994 36.6673 36.6673C38.1994 35.1351 39.4148 33.3161 40.244 31.3143C41.0732 29.3124 41.5 27.1668 41.5 25C41.5 22.8332 41.0732 20.6876 40.244 18.6857C39.4148 16.6839 38.1994 14.8649 36.6673 13.3327C35.1351 11.8006 33.3161 10.5852 31.3143 9.75599C29.3124 8.92679 27.1668 8.5 25 8.5C22.8332 8.5 20.6876 8.92679 18.6857 9.75599C16.6839 10.5852 14.8649 11.8006 13.3327 13.3327C11.8006 14.8649 10.5852 16.6839 9.75599 18.6857C8.92678 20.6876 8.5 22.8332 8.5 25Z" fill="white" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M17.666 25C17.666 29.3761 18.4386 33.5729 19.8139 36.6673C21.1892 39.7616 23.0544 41.5 24.9993 41.5C26.9443 41.5 28.8095 39.7616 30.1848 36.6673C31.5601 33.5729 32.3327 29.3761 32.3327 25C32.3327 20.6239 31.5601 16.4271 30.1848 13.3327C28.8095 10.2384 26.9443 8.5 24.9993 8.5C23.0544 8.5 21.1892 10.2384 19.8139 13.3327C18.4386 16.4271 17.666 20.6239 17.666 25Z" fill="white" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8.5 25.0003C8.5 29.052 15.8883 32.3337 25 32.3337C34.1117 32.3337 41.5 29.052 41.5 25.0003C41.5 20.9487 34.1117 17.667 25 17.667C15.8883 17.667 8.5 20.9487 8.5 25.0003Z" fill="white" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M17.666 25C17.666 29.3761 18.4386 33.5729 19.8139 36.6673C21.1892 39.7616 23.0544 41.5 24.9993 41.5C26.9443 41.5 28.8095 39.7616 30.1848 36.6673C31.5601 33.5729 32.3327 29.3761 32.3327 25C32.3327 20.6239 31.5601 16.4271 30.1848 13.3327C28.8095 10.2384 26.9443 8.5 24.9993 8.5C23.0544 8.5 21.1892 10.2384 19.8139 13.3327C18.4386 16.4271 17.666 20.6239 17.666 25Z" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path></svg>',
|
|
124
|
+
schema: "cd26d36b.create-schema.json",
|
|
125
|
+
tools: "d7989384.create-tools.json",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "operations-gateway",
|
|
129
|
+
title: "Operations Gateway",
|
|
130
|
+
basePath: "/operations-gateway",
|
|
131
|
+
projectId: "hzao7xsp",
|
|
132
|
+
dataset: "production",
|
|
133
|
+
icon: '<svg width="50" height="49" viewBox="0 0 50 49" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="50" height="49" fill="#FFCC32"></rect><path d="M30.5 36.6667V33C30.5 31.5413 29.9205 30.1424 28.8891 29.1109C27.8576 28.0795 26.4587 27.5 25 27.5C23.5413 27.5 22.1424 28.0795 21.1109 29.1109C20.0795 30.1424 19.5 31.5413 19.5 33V36.6667C19.5 37.1529 19.3068 37.6192 18.963 37.963C18.6192 38.3068 18.1529 38.5 17.6667 38.5H10.3333C9.8471 38.5 9.38079 38.3068 9.03697 37.963C8.69315 37.6192 8.5 37.1529 8.5 36.6667V11H15.8333V16.5H21.3333V11H28.6667V16.5H34.1667V11H41.5V36.6667C41.5 37.1529 41.3068 37.6192 40.963 37.963C40.6192 38.3068 40.1529 38.5 39.6667 38.5H32.3333C31.8471 38.5 31.3808 38.3068 31.037 37.963C30.6932 37.6192 30.5 37.1529 30.5 36.6667Z" fill="white" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8.5 22H41.5Z" fill="white"></path><path d="M8.5 22H41.5" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path></svg>',
|
|
134
|
+
schema: "cd26d36b.create-schema.json",
|
|
135
|
+
tools: "f63673b7.create-tools.json",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "finder",
|
|
139
|
+
title: "Finder",
|
|
140
|
+
basePath: "/finder",
|
|
141
|
+
projectId: "hzao7xsp",
|
|
142
|
+
dataset: "production",
|
|
143
|
+
icon: '<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="50" height="50" fill="#D6EDFF"></rect><rect x="12.2959" y="11.7402" width="25.41" height="13.86" fill="white"></rect><path d="M23.0732 11.356H26.9232Z" fill="white"></path><path d="M23.0732 11.356H26.9232" stroke="black" stroke-width="3.85" stroke-linecap="round" stroke-linejoin="round"></path><path d="M38.4734 24.8306C37.1895 35.0966 33.9824 40.2306 28.8484 40.2306H21.1484C16.0145 40.2306 12.8074 35.0966 11.5234 24.8306" fill="white"></path><path d="M38.4734 24.8306C37.1895 35.0966 33.9824 40.2306 28.8484 40.2306H21.1484C16.0145 40.2306 12.8074 35.0966 11.5234 24.8306" stroke="black" stroke-width="3.85" stroke-linecap="round" stroke-linejoin="round"></path><path d="M23.0732 32.5303C23.0732 33.8142 23.7143 34.4553 24.9982 34.4553C26.2822 34.4553 26.9232 33.8142 26.9232 32.5303H23.0732Z" fill="white" stroke="black" stroke-width="3.85" stroke-linecap="round" stroke-linejoin="round"></path><path d="M24.999 36.3804V40.2304Z" fill="white"></path><path d="M24.999 36.3804V40.2304" stroke="black" stroke-width="3.85" stroke-linecap="round" stroke-linejoin="round"></path><path d="M21.1494 22.9058V22.9241Z" fill="white"></path><path d="M21.1494 22.9058V22.9241" stroke="black" stroke-width="3.85" stroke-linecap="round" stroke-linejoin="round"></path><path d="M28.8486 22.9058V22.9241Z" fill="white"></path><path d="M28.8486 22.9058V22.9241" stroke="black" stroke-width="3.85" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11.5239 9.43018L23.0739 11.2974L11.0657 24.1718C10.7284 24.551 10.2575 24.7849 9.7516 24.8248C9.24568 24.8646 8.74398 24.7072 8.35148 24.3855C8.08529 24.1689 7.88519 23.882 7.77392 23.5574C7.66265 23.2327 7.64466 22.8834 7.72201 22.549L11.5239 9.43018Z" fill="white" stroke="black" stroke-width="3.85" stroke-linecap="round" stroke-linejoin="round"></path><path d="M38.4748 9.43018L26.9248 11.2974L38.933 24.1718C39.6221 24.9572 40.8368 25.0535 41.6472 24.3855C41.9134 24.1689 42.1135 23.882 42.2248 23.5574C42.336 23.2327 42.354 22.8834 42.2767 22.549L38.4748 9.43018Z" fill="white" stroke="black" stroke-width="3.85" stroke-linecap="round" stroke-linejoin="round"></path></svg>',
|
|
144
|
+
schema: "cd26d36b.create-schema.json",
|
|
145
|
+
tools: "790d30d6.create-tools.json",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "radar",
|
|
149
|
+
title: "Radar",
|
|
150
|
+
basePath: "/radar",
|
|
151
|
+
projectId: "hzao7xsp",
|
|
152
|
+
dataset: "production",
|
|
153
|
+
icon: '<svg viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="50" height="50" fill="#CDEA19"></rect><path d="M41.4925 24.9818H26.8258C26.8258 24.6192 26.7183 24.2647 26.5169 23.9632C26.3154 23.6617 26.0291 23.4268 25.6941 23.288C25.3591 23.1492 24.9905 23.1129 24.6348 23.1837C24.2792 23.2544 23.9525 23.429 23.6962 23.6854C23.4398 23.9418 23.2651 24.2685 23.1944 24.6241C23.1237 24.9797 23.16 25.3484 23.2987 25.6834C23.4375 26.0184 23.6725 26.3047 23.974 26.5061C24.2755 26.7076 24.6299 26.8151 24.9925 26.8151V41.4818C29.3686 41.4818 33.5654 39.7434 36.6598 36.649C39.7541 33.5547 41.4925 29.3578 41.4925 24.9818Z" fill="white" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M32.3252 19.4821C31.5372 18.4314 30.5326 17.5624 29.3795 16.9337C28.2264 16.3051 26.9517 15.9316 25.6417 15.8385C24.3317 15.7454 23.0169 15.9349 21.7865 16.3941C20.5561 16.8534 19.4387 17.5716 18.5101 18.5003C17.5814 19.4289 16.8631 20.5463 16.4039 21.7767C15.9446 23.0072 15.7552 24.3219 15.8483 25.6319C15.9414 26.9419 16.3149 28.2167 16.9435 29.3698C17.5721 30.5229 18.4412 31.5274 19.4919 32.3154" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M40.55 19.4821C39.6182 16.8561 38.0365 14.5088 35.9524 12.6592C33.8683 10.8096 31.3498 9.51795 28.6317 8.90464C25.9136 8.29133 23.0845 8.37637 20.4081 9.15184C17.7317 9.9273 15.2953 11.3679 13.3261 13.3393C11.3569 15.3108 9.91908 17.7488 9.14665 20.426C8.37422 23.1033 8.29238 25.9325 8.90876 28.6499C9.52514 31.3673 10.8197 33.8844 12.6716 35.9664C14.5236 38.0483 16.8726 39.6274 19.4997 40.5562" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path></svg>',
|
|
154
|
+
schema: "cd26d36b.create-schema.json",
|
|
155
|
+
tools: "6e3a5662.create-tools.json",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "atlas",
|
|
159
|
+
title: "Atlas",
|
|
160
|
+
basePath: "/atlas",
|
|
161
|
+
projectId: "hzao7xsp",
|
|
162
|
+
dataset: "production",
|
|
163
|
+
icon: '<svg width="50" height="49" viewBox="0 0 50 49" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="50" height="49" fill="#40E8AF"></rect><path d="M17.667 31.8332L21.3337 20.8332L32.3337 17.1665L28.667 28.1665L17.667 31.8332Z" fill="white" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8.5 24.5C8.5 26.6668 8.92678 28.8124 9.75599 30.8143C10.5852 32.8161 11.8006 34.6351 13.3327 36.1673C14.8649 37.6994 16.6839 38.9148 18.6857 39.744C20.6876 40.5732 22.8332 41 25 41C27.1668 41 29.3124 40.5732 31.3143 39.744C33.3161 38.9148 35.1351 37.6994 36.6673 36.1673C38.1994 34.6351 39.4148 32.8161 40.244 30.8143C41.0732 28.8124 41.5 26.6668 41.5 24.5C41.5 22.3332 41.0732 20.1876 40.244 18.1857C39.4148 16.1839 38.1994 14.3649 36.6673 12.8327C35.1351 11.3006 33.3161 10.0852 31.3143 9.25599C29.3124 8.42679 27.1668 8 25 8C22.8332 8 20.6876 8.42679 18.6857 9.25599C16.6839 10.0852 14.8649 11.3006 13.3327 12.8327C11.8006 14.3649 10.5852 16.1839 9.75599 18.1857C8.92678 20.1876 8.5 22.3332 8.5 24.5Z" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M25 8V11.6667" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M25 37.3335V41.0002" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8.5 24.5H12.1667" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path><path d="M37.833 24.5H41.4997" stroke="black" stroke-width="3.66667" stroke-linecap="round" stroke-linejoin="round"></path></svg>',
|
|
164
|
+
schema: "b0fefb08.create-schema.json",
|
|
165
|
+
tools: "9597875e.create-tools.json",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "resources",
|
|
169
|
+
title: "Resource Center",
|
|
170
|
+
basePath: "/resources",
|
|
171
|
+
projectId: "hzao7xsp",
|
|
172
|
+
dataset: "production",
|
|
173
|
+
icon: '<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3 19a9 9 0 0 1 9 0a9 9 0 0 1 9 0"></path><path d="M3 6a9 9 0 0 1 9 0a9 9 0 0 1 9 0"></path><path d="M3 6l0 13"></path><path d="M12 6l0 13"></path><path d="M21 6l0 13"></path></svg>',
|
|
174
|
+
schema: "b0fefb08.create-schema.json",
|
|
175
|
+
tools: "c704250f.create-tools.json",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "data",
|
|
179
|
+
title: "Data Hub",
|
|
180
|
+
basePath: "/data",
|
|
181
|
+
projectId: "hzao7xsp",
|
|
182
|
+
dataset: "production",
|
|
183
|
+
icon: '<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M9 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-12a2 2 0 0 0 -2 -2h-2"></path><path d="M9 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z"></path><path d="M9 17v-4"></path><path d="M12 17v-1"></path><path d="M15 17v-2"></path><path d="M12 17v-1"></path></svg>',
|
|
184
|
+
schema: "b0fefb08.create-schema.json",
|
|
185
|
+
tools: "08382af6.create-tools.json",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: "operations",
|
|
189
|
+
title: "SupportOS\u2122",
|
|
190
|
+
basePath: "/operations",
|
|
191
|
+
projectId: "hzao7xsp",
|
|
192
|
+
dataset: "production",
|
|
193
|
+
icon: '<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M12 8a2 2 0 0 1 2 2v4a2 2 0 1 1 -4 0v-4a2 2 0 0 1 2 -2"></path><path d="M17 15c.345 .6 1.258 1 2 1a2 2 0 1 0 0 -4a2 2 0 1 1 0 -4c.746 0 1.656 .394 2 1"></path><path d="M3 15c.345 .6 1.258 1 2 1a2 2 0 1 0 0 -4a2 2 0 1 1 0 -4c.746 0 1.656 .394 2 1"></path></svg>',
|
|
194
|
+
schema: "cd26d36b.create-schema.json",
|
|
195
|
+
tools: "a6046e2d.create-tools.json",
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
config: {},
|
|
201
|
+
} satisfies StudioApplicationApiResponse;
|
|
202
|
+
|
|
203
|
+
export const SINGLE_WORKSPACE_STUDIO_DATA = {
|
|
204
|
+
id: "0092b48439cd2aa19de05c2c",
|
|
205
|
+
projectId: "c16r74n4",
|
|
206
|
+
organizationId: null,
|
|
207
|
+
title: null,
|
|
208
|
+
type: "studio",
|
|
209
|
+
urlType: "internal",
|
|
210
|
+
appHost: "sanity-home",
|
|
211
|
+
dashboardStatus: "default",
|
|
212
|
+
createdAt: "2024-08-09T14:04:44.805Z",
|
|
213
|
+
updatedAt: "2025-09-19T08:33:10.922Z",
|
|
214
|
+
autoUpdatingVersion: "latest",
|
|
215
|
+
activeDeployment: {
|
|
216
|
+
id: "g8cp3s08izh6to03l0sycxfr",
|
|
217
|
+
version: "4.9.0",
|
|
218
|
+
isActiveDeployment: true,
|
|
219
|
+
userApplicationId: "0092b48439cd2aa19de05c2c",
|
|
220
|
+
isAutoUpdating: true,
|
|
221
|
+
size: 1433774,
|
|
222
|
+
deployedAt: "2025-09-19T08:16:13.060Z",
|
|
223
|
+
deployedBy: "go2WapeAw",
|
|
224
|
+
createdAt: "2025-09-19T08:16:12.930Z",
|
|
225
|
+
updatedAt: "2025-09-19T08:16:13.060Z",
|
|
226
|
+
manifest: null,
|
|
227
|
+
},
|
|
228
|
+
manifest: null,
|
|
229
|
+
manifestData: {
|
|
230
|
+
value: {
|
|
231
|
+
version: 2,
|
|
232
|
+
createdAt: "2025-09-19T08:16:12.185Z",
|
|
233
|
+
workspaces: [
|
|
234
|
+
{
|
|
235
|
+
name: "default",
|
|
236
|
+
title: "Sanity Home",
|
|
237
|
+
basePath: "/admin",
|
|
238
|
+
projectId: "c16r74n4",
|
|
239
|
+
dataset: "production",
|
|
240
|
+
icon: '<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1_1391)"><rect width="256" height="256" fill="white"></rect><rect width="256" height="256" fill="white"></rect><g clip-path="url(#clip1_1_1391)"><path d="M215.759 152.483L208.799 140.366L175.13 160.88L212.526 113.252L218.179 109.933L216.78 107.831L219.349 104.548L207.549 94.7227L202.147 101.608L93.1263 165.414L133.434 116.925L208.512 75.7566L201.379 61.963L160.486 84.3775L180.623 60.168L169.087 50L123.767 104.513L78.7575 129.206L113.217 83.6335L134.811 72.3909L127.953 58.4438L65.0424 91.2034L82.1978 68.4937L70.2143 58.8926L34 106.839L34.5619 107.288L41.3277 121.07L81.4753 100.155L44.8826 148.539L50.8801 153.345L54.4465 160.242L96.7156 137.06L50.1691 193.061L61.7054 203.229L64.0218 200.442L176.311 134.509L139.031 182.007L139.638 182.515L139.581 182.55L147.31 196.001L196.895 165.781L177.802 196.603L190.6 205L221 155.931L215.759 152.483Z" fill="#0B0B0B"></path></g></g><defs><clipPath id="clip0_1_1391"><rect width="256" height="256" fill="white"></rect></clipPath><clipPath id="clip1_1_1391"><rect width="187" height="155" fill="white" transform="translate(34 50)"></rect></clipPath></defs></svg>',
|
|
241
|
+
schema: "030a38ac.create-schema.json",
|
|
242
|
+
tools: "10657faf.create-tools.json",
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
config: {},
|
|
248
|
+
} satisfies StudioApplicationApiResponse;
|