@sanity/workbench-cli 2.0.2 → 2.2.1
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 +1 -1
- package/dist/_exports/_internal_render.d.ts +12 -0
- package/dist/_exports/_internal_render.js +3 -0
- package/dist/_exports/_internal_render.js.map +1 -0
- package/dist/_exports/build.d.ts +4 -4
- package/dist/_exports/build.js +2 -1
- package/dist/_exports/build.js.map +1 -1
- package/dist/_exports/defineApp-DldjQwq6.d.ts +375 -0
- package/dist/_exports/deploy.d.ts +10 -9
- package/dist/_exports/deploy.js +1 -0
- package/dist/_exports/deploy.js.map +1 -1
- package/dist/_exports/dev.d.ts +1 -1
- package/dist/_exports/index.d.ts +23 -16
- package/dist/_exports/index.js +2 -1
- package/dist/_exports/index.js.map +1 -1
- package/dist/_exports/preview.d.ts +1 -1
- package/dist/_exports/{registry-DI7hnTof.d.ts → registry-HE_dEHFa.d.ts} +38 -15
- package/dist/_exports/resolveWorkbenchConfig-6iznHbED.d.ts +60 -0
- package/dist/_exports/{summarizeInterfaces-DBGiAwNT.d.ts → summarizeInterfaces-CjkeWwkY.d.ts} +20 -17
- package/dist/_exports/undeploy.d.ts +12 -9
- package/dist/actions/build/artifact.js +1 -1
- package/dist/actions/build/artifact.js.map +1 -1
- package/dist/actions/build/views/artifact.js +1 -1
- package/dist/actions/build/views/artifact.js.map +1 -1
- package/dist/actions/build/vite/optimize-deps.js +3 -3
- package/dist/actions/build/vite/optimize-deps.js.map +1 -1
- package/dist/actions/build/vite/plugins/plugin-module-federation.js +8 -17
- package/dist/actions/build/vite/plugins/plugin-module-federation.js.map +1 -1
- package/dist/actions/build/vite/plugins/plugin-sanity-federation-runtime.js +1 -1
- package/dist/actions/build/vite/plugins/plugin-sanity-federation-runtime.js.map +1 -1
- package/dist/actions/build/vite/workbench-vite-plugins.js +1 -1
- package/dist/actions/build/vite/workbench-vite-plugins.js.map +1 -1
- package/dist/actions/deploy/deployPayload.js +0 -3
- package/dist/actions/deploy/deployPayload.js.map +1 -1
- package/dist/actions/deploy/deployWorkbenchApp.js +46 -4
- package/dist/actions/deploy/deployWorkbenchApp.js.map +1 -1
- package/dist/actions/deploy/getWorkbench.js +4 -5
- package/dist/actions/deploy/getWorkbench.js.map +1 -1
- package/dist/actions/deploy/summarizeInterfaces.js +14 -10
- package/dist/actions/deploy/summarizeInterfaces.js.map +1 -1
- package/dist/actions/deploy/viewDeployment.js +17 -5
- package/dist/actions/deploy/viewDeployment.js.map +1 -1
- package/dist/actions/dev/deriveConfigs.js +9 -6
- package/dist/actions/dev/deriveConfigs.js.map +1 -1
- package/dist/actions/dev/exposesSetId.js +2 -2
- package/dist/actions/dev/exposesSetId.js.map +1 -1
- package/dist/actions/dev/registry.js +31 -25
- package/dist/actions/dev/registry.js.map +1 -1
- package/dist/actions/dev/startDevManifestWatcher.js.map +1 -1
- package/dist/actions/dev/startDevServerRegistration.js +35 -60
- package/dist/actions/dev/startDevServerRegistration.js.map +1 -1
- package/dist/actions/dev/startWorkbenchDevServer.js +15 -33
- package/dist/actions/dev/startWorkbenchDevServer.js.map +1 -1
- package/dist/actions/dev/writeWorkbenchRuntime.js +2 -2
- package/dist/actions/dev/writeWorkbenchRuntime.js.map +1 -1
- package/dist/actions/preview/serveBuiltApplication.js +1 -1
- package/dist/actions/preview/serveBuiltApplication.js.map +1 -1
- package/dist/actions/preview/startWorkbenchPreview.js +1 -1
- package/dist/actions/preview/startWorkbenchPreview.js.map +1 -1
- package/dist/actions/undeploy/workbenchUndeployAdapter.js +24 -33
- package/dist/actions/undeploy/workbenchUndeployAdapter.js.map +1 -1
- package/dist/appId.js +13 -9
- package/dist/appId.js.map +1 -1
- package/dist/applicationReference.js +12 -0
- package/dist/applicationReference.js.map +1 -0
- package/dist/contract.js +69 -31
- package/dist/contract.js.map +1 -1
- package/dist/defineApp.js +53 -63
- package/dist/defineApp.js.map +1 -1
- package/dist/defineView.js +3 -3
- package/dist/defineView.js.map +1 -1
- package/dist/deriveInterfaces.js +45 -19
- package/dist/deriveInterfaces.js.map +1 -1
- package/dist/renderDashboard.js +63 -0
- package/dist/renderDashboard.js.map +1 -0
- package/dist/resolveWorkbenchApp.js +13 -9
- package/dist/resolveWorkbenchApp.js.map +1 -1
- package/dist/resolveWorkbenchConfig.js +30 -0
- package/dist/resolveWorkbenchConfig.js.map +1 -0
- package/dist/services/applications.js +7 -1
- package/dist/services/applications.js.map +1 -1
- package/package.json +12 -6
- package/dist/_exports/contract-DyG11fQ7.d.ts +0 -45
- package/dist/_exports/defineApp-r_CmoZfS.d.ts +0 -169
- package/dist/_exports/resolveWorkbenchApp-DqFqET9E.d.ts +0 -41
package/dist/defineApp.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod/mini';
|
|
2
2
|
import { APP_SLUG_PATTERN } from './appSlug.js';
|
|
3
|
-
import {
|
|
3
|
+
import { DockSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } from './contract.js';
|
|
4
4
|
/**
|
|
5
5
|
* Dashboard visibility values. Mirrors `APP_VISIBILITIES` in `@sanity/cli-core`
|
|
6
6
|
* (which can't be imported here — pulling the barrel into this lean module bloats
|
|
@@ -17,16 +17,10 @@ import { ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } fr
|
|
|
17
17
|
'coreApp',
|
|
18
18
|
'studio',
|
|
19
19
|
'canvas',
|
|
20
|
-
'dashboard'
|
|
21
|
-
'media-library'
|
|
22
|
-
]);
|
|
23
|
-
/** Dock groups an app can place itself into. */ const DockGroupSchema = z.enum([
|
|
24
|
-
'dock.system',
|
|
25
|
-
'dock.applications',
|
|
26
|
-
'dock.user'
|
|
20
|
+
'dashboard'
|
|
27
21
|
]);
|
|
28
22
|
/**
|
|
29
|
-
* Runtime-validation schema for `
|
|
23
|
+
* Runtime-validation schema for `defineApplication`.
|
|
30
24
|
* @internal
|
|
31
25
|
*/ export const DefineAppInputSchema = z.object({
|
|
32
26
|
/**
|
|
@@ -34,31 +28,34 @@ import { ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } fr
|
|
|
34
28
|
* from the public `DefineAppInput` type.
|
|
35
29
|
* @internal
|
|
36
30
|
*/ applicationType: z.optional(ApplicationType),
|
|
37
|
-
/**
|
|
38
|
-
* Deployed as a versioned snapshot on the app's org installation, not the
|
|
39
|
-
* application service. Singletons only. Internal, so excluded from the public
|
|
40
|
-
* `DefineAppInput` and set via `@ts-expect-error` like `applicationType`.
|
|
41
|
-
* @internal
|
|
42
|
-
*/ config: z.optional(ConfigSchema),
|
|
31
|
+
/** Default placement inherited by panel and window views. */ dock: z.optional(DockSchema),
|
|
43
32
|
/**
|
|
44
33
|
* App entrypoint module. Defaults to `./src/App.tsx` when omitted. The build
|
|
45
34
|
* derives the app's navigable `app` view from it. SDK apps only — setting it
|
|
46
35
|
* on a studio is rejected (studio app views are not yet implemented).
|
|
47
36
|
*/ entry: z.optional(z.string("must be a path to the app's entry file")),
|
|
48
|
-
/** Dock group to render in. Defaults to `dock.applications` when omitted. */ group: z.optional(DockGroupSchema),
|
|
49
37
|
/** Optional icon override (path to an SVG). Wins over manifest/studio icon. */ icon: z.optional(z.string()),
|
|
50
38
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
/** Background services the app runs (e.g. a `worker` emitting dock badges). */ services: z.optional(z.array(ServiceDeclarationSchema, 'must be an array of services').check(z.refine((services)=>new Set(services.map((service)=>service.name)).size === services.length, 'Service `name` must be unique within an app'))),
|
|
39
|
+
* Stable identity, distinct from the `slug` address. Defaults to `slug` when
|
|
40
|
+
* omitted. Identity keys — the build id and derived interface ids — are built
|
|
41
|
+
* from this, so renaming the slug no longer changes identity.
|
|
42
|
+
*/ name: z.optional(z.string().check(z.regex(APP_SLUG_PATTERN, 'App `name` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric'))),
|
|
43
|
+
/** Organization that owns the app — the workbench runs and deploys against it. */ organizationId: z.string("App `organizationId` is required — pass the owning organization's ID to `defineApplication`"),
|
|
57
44
|
slug: z.string('App `slug` is required — the hostname the application is created at on deploy').check(z.regex(APP_SLUG_PATTERN, 'App `slug` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric')),
|
|
58
45
|
/** User-facing app title. Wins over studio.config.ts title on merge. */ title: z.string(),
|
|
59
|
-
/** Views the app exposes (e.g. dock panels). */ views: z.optional(z.array(InterfaceDeclarationSchema, 'must be an array of
|
|
60
|
-
/** Dashboard visibility of the app. Defaults to `default` when omitted. */ visibility: z.optional(z.enum(APP_VISIBILITIES))
|
|
61
|
-
|
|
46
|
+
/** Views the app exposes (e.g. a window, dock panels, and tiles). */ views: z.optional(z.array(InterfaceDeclarationSchema, 'must be an array of views')),
|
|
47
|
+
/** Dashboard visibility of the app. Defaults to `default` when omitted. */ visibility: z.optional(z.enum(APP_VISIBILITIES)),
|
|
48
|
+
/** Background web workers the app runs. */ webWorkers: z.optional(z.array(ServiceDeclarationSchema, 'must be an array of web workers'))
|
|
49
|
+
}).check(z.refine((input)=>{
|
|
50
|
+
const names = [
|
|
51
|
+
...input.entry === undefined ? [] : [
|
|
52
|
+
input.name ?? input.slug
|
|
53
|
+
],
|
|
54
|
+
...(input.views ?? []).map((view)=>view.name),
|
|
55
|
+
...(input.webWorkers ?? []).map((webWorker)=>webWorker.name)
|
|
56
|
+
];
|
|
57
|
+
return new Set(names).size === names.length;
|
|
58
|
+
}, '`name` must be unique across views and web workers. Rename one of the duplicates.'), // Studio app views are not implemented yet. A studio that declares `entry`
|
|
62
59
|
// (the SDK app-view entrypoint) is rejected here rather than silently
|
|
63
60
|
// generating one; studios keep navigating via their existing render path.
|
|
64
61
|
z.refine((input)=>!(input.applicationType === 'studio' && input.entry !== undefined), {
|
|
@@ -66,18 +63,7 @@ z.refine((input)=>!(input.applicationType === 'studio' && input.entry !== undefi
|
|
|
66
63
|
path: [
|
|
67
64
|
'entry'
|
|
68
65
|
]
|
|
69
|
-
}))
|
|
70
|
-
// Library). A non-singleton declaring one is rejected — see
|
|
71
|
-
// {@link readConfig} for the runtime guard.
|
|
72
|
-
z.refine((input)=>!(input.config && !input.isSingleton), {
|
|
73
|
-
error: '`config` is only supported for singleton apps',
|
|
74
|
-
path: [
|
|
75
|
-
'config'
|
|
76
|
-
]
|
|
77
|
-
})).check(// A navigable app view (`entry`) and dock panels are the mutually-exclusive
|
|
78
|
-
// navigable kinds. An `asset_source` view is a separate kind — a picker
|
|
79
|
-
// brokered to other apps — so it may sit alongside either.
|
|
80
|
-
z.refine((input)=>!(input.entry !== undefined && (input.views?.some((view)=>view.type === 'panel') ?? false)), 'An app cannot expose both an app view (`entry`) and panel views. Declare one or the other.')).check(z.refine((input)=>(input.views?.filter((view)=>view.type === 'panel').length ?? 0) <= 1, 'An app can expose at most one panel view.'));
|
|
66
|
+
}));
|
|
81
67
|
/**
|
|
82
68
|
* Nominal brand the CLI discriminates on to enable the workbench build/deploy
|
|
83
69
|
* codepath. Registered via `Symbol.for` so the marker survives module-realm
|
|
@@ -85,31 +71,19 @@ z.refine((input)=>!(input.entry !== undefined && (input.views?.some((view)=>view
|
|
|
85
71
|
* `Symbol.for` rather than importing it, so it stays internal to this module.
|
|
86
72
|
*/ const WORKBENCH_APP = Symbol.for('sanity.workbench.defineApp');
|
|
87
73
|
/**
|
|
88
|
-
* Whether `app` is a branded `
|
|
74
|
+
* Whether `app` is a branded `defineApplication(...)` result — the sole
|
|
89
75
|
* workbench opt-in.
|
|
90
76
|
* @public
|
|
91
77
|
*/ export function isWorkbenchApp(app) {
|
|
92
78
|
return typeof app === 'object' && app !== null && WORKBENCH_APP in app;
|
|
93
79
|
}
|
|
94
|
-
/**
|
|
95
|
-
* The app's config, or `undefined` when it declares none. Throws
|
|
96
|
-
* when a non-singleton declares one — configs belong to Sanity-owned singletons,
|
|
97
|
-
* so build/dev/deploy all read it through here to reject the combination
|
|
98
|
-
* consistently.
|
|
99
|
-
* @internal
|
|
100
|
-
*/ export function readConfig(app) {
|
|
101
|
-
if (app.config && !app.isSingleton) {
|
|
102
|
-
throw new Error('`config` is only supported for singleton apps');
|
|
103
|
-
}
|
|
104
|
-
return app.config;
|
|
105
|
-
}
|
|
106
80
|
/**
|
|
107
81
|
* Declare a Sanity Workbench application. Identity at runtime — returns the same
|
|
108
82
|
* object reference, tagged with the workbench brand. Field validation (the
|
|
109
83
|
* `slug` pattern etc.) runs at build time in the CLI via `DefineAppInputSchema`;
|
|
110
84
|
* this helper stays a thin, pure identity wrapper.
|
|
111
85
|
* @public
|
|
112
|
-
*/ export function
|
|
86
|
+
*/ export function defineApplication(input) {
|
|
113
87
|
return Object.defineProperty(input, WORKBENCH_APP, {
|
|
114
88
|
configurable: false,
|
|
115
89
|
enumerable: false,
|
|
@@ -118,20 +92,36 @@ z.refine((input)=>!(input.entry !== undefined && (input.views?.some((view)=>view
|
|
|
118
92
|
});
|
|
119
93
|
}
|
|
120
94
|
/**
|
|
121
|
-
*
|
|
95
|
+
* Nominal brand the CLI discriminates a config on. A config is *not* an app: it
|
|
96
|
+
* carries no `slug`/`title`/application identity, only a target `appType`, its
|
|
97
|
+
* owning `organizationId`, and its `fields`. Registered via `Symbol.for` so the
|
|
98
|
+
* marker survives module-realm boundaries — `@sanity/cli-core` re-derives the
|
|
99
|
+
* same global symbol rather than importing it.
|
|
100
|
+
*/ const WORKBENCH_CONFIG = Symbol.for('sanity.workbench.defineConfig');
|
|
101
|
+
/**
|
|
102
|
+
* Whether `app` is a branded `unstable_defineMediaLibrary(...)` config — a
|
|
103
|
+
* config-carrier rather than an app. Configs live in `cliConfig.app` alongside
|
|
104
|
+
* apps but resolve through `resolveWorkbenchConfig`, not `resolveWorkbenchApp`.
|
|
105
|
+
* @public
|
|
106
|
+
*/ export function isWorkbenchConfig(app) {
|
|
107
|
+
return typeof app === 'object' && app !== null && WORKBENCH_CONFIG in app;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Declare the Sanity Media Library config. The Media Library is a Sanity-owned
|
|
111
|
+
* singleton, so this is not an app — it carries no slug or title, only the
|
|
112
|
+
* target `appType`, its owning organization, and the `fields` it contributes to
|
|
113
|
+
* the installation's config.
|
|
122
114
|
* @public
|
|
123
115
|
*/ export function unstable_defineMediaLibrary(input) {
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
slug: 'media-library',
|
|
134
|
-
title: 'Media Library'
|
|
116
|
+
return Object.defineProperty({
|
|
117
|
+
appType: 'media-library',
|
|
118
|
+
fields: input.fields ?? [],
|
|
119
|
+
organizationId: input.organizationId
|
|
120
|
+
}, WORKBENCH_CONFIG, {
|
|
121
|
+
configurable: false,
|
|
122
|
+
enumerable: false,
|
|
123
|
+
value: true,
|
|
124
|
+
writable: false
|
|
135
125
|
});
|
|
136
126
|
}
|
|
137
127
|
|
package/dist/defineApp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/defineApp.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\nimport {APP_SLUG_PATTERN} from './appSlug.js'\nimport {ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema} from './contract.js'\n\n/**\n * Dashboard visibility values. Mirrors `APP_VISIBILITIES` in `@sanity/cli-core`\n * (which can't be imported here — pulling the barrel into this lean module bloats\n * the config-load path). Kept in sync by a type test in `defineApp.test.ts`.\n */\nconst APP_VISIBILITIES = ['default', 'unlisted', 'disabled'] as const\n\n/**\n * Internal application discriminator. Sanity-owned singleton apps only;\n * validated by the schema but excluded from the public `DefineAppInput` type.\n */\nconst ApplicationType = z.enum(['coreApp', 'studio', 'canvas', 'dashboard', 'media-library'])\n\n/** Dock groups an app can place itself into. */\nconst DockGroupSchema = z.enum(['dock.system', 'dock.applications', 'dock.user'])\n\n/**\n * Dock group identifier. The API does not block a user app from declaring a\n * reserved group (e.g. `dock.system`); priority conventions keep Sanity-owned\n * apps ahead.\n * @public\n */\nexport type DockGroup = z.output<typeof DockGroupSchema>\n\n/**\n * Runtime-validation schema for `unstable_defineApp`.\n * @internal\n */\nexport const DefineAppInputSchema = z\n .object({\n /**\n * Internal — Sanity-owned singleton apps only. Validated here but excluded\n * from the public `DefineAppInput` type.\n * @internal\n */\n applicationType: z.optional(ApplicationType),\n /**\n * Deployed as a versioned snapshot on the app's org installation, not the\n * application service. Singletons only. Internal, so excluded from the public\n * `DefineAppInput` and set via `@ts-expect-error` like `applicationType`.\n * @internal\n */\n config: z.optional(ConfigSchema),\n /**\n * App entrypoint module. Defaults to `./src/App.tsx` when omitted. The build\n * derives the app's navigable `app` view from it. SDK apps only — setting it\n * on a studio is rejected (studio app views are not yet implemented).\n */\n entry: z.optional(z.string(\"must be a path to the app's entry file\")),\n /** Dock group to render in. Defaults to `dock.applications` when omitted. */\n group: z.optional(DockGroupSchema),\n /** Optional icon override (path to an SVG). Wins over manifest/studio icon. */\n icon: z.optional(z.string()),\n /**\n * Sanity-owned app deployed once, installed per org; excluded from the public `DefineAppInput`.\n * @internal\n */\n isSingleton: z.optional(z.boolean()),\n /** Organization that owns the app — the workbench runs and deploys against it. */\n organizationId: z.string(\n \"App `organizationId` is required — pass the owning organization's ID to `unstable_defineApp`\",\n ),\n /** Sort position within the group, ascending. Defaults to `100` when omitted. */\n priority: z.optional(z.number()),\n /** Background services the app runs (e.g. a `worker` emitting dock badges). */\n services: z.optional(\n z\n .array(ServiceDeclarationSchema, 'must be an array of services')\n .check(\n z.refine(\n (services) => new Set(services.map((service) => service.name)).size === services.length,\n 'Service `name` must be unique within an app',\n ),\n ),\n ),\n slug: z\n .string('App `slug` is required — the hostname the application is created at on deploy')\n .check(\n z.regex(\n APP_SLUG_PATTERN,\n 'App `slug` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric',\n ),\n ),\n /** User-facing app title. Wins over studio.config.ts title on merge. */\n title: z.string(),\n /** Views the app exposes (e.g. dock panels). */\n views: z.optional(\n z\n .array(InterfaceDeclarationSchema, 'must be an array of panels')\n .check(\n z.refine(\n (views) => new Set(views.map((view) => view.name)).size === views.length,\n 'View `name` must be unique within an app',\n ),\n ),\n ),\n /** Dashboard visibility of the app. Defaults to `default` when omitted. */\n visibility: z.optional(z.enum(APP_VISIBILITIES)),\n })\n .check(\n // Studio app views are not implemented yet. A studio that declares `entry`\n // (the SDK app-view entrypoint) is rejected here rather than silently\n // generating one; studios keep navigating via their existing render path.\n z.refine((input) => !(input.applicationType === 'studio' && input.entry !== undefined), {\n error: 'App views for studios are not implemented yet',\n path: ['entry'],\n }),\n )\n .check(\n // An config belongs to a Sanity-owned singleton (the Media\n // Library). A non-singleton declaring one is rejected — see\n // {@link readConfig} for the runtime guard.\n z.refine((input) => !(input.config && !input.isSingleton), {\n error: '`config` is only supported for singleton apps',\n path: ['config'],\n }),\n )\n .check(\n // A navigable app view (`entry`) and dock panels are the mutually-exclusive\n // navigable kinds. An `asset_source` view is a separate kind — a picker\n // brokered to other apps — so it may sit alongside either.\n z.refine(\n (input) =>\n !(\n input.entry !== undefined &&\n (input.views?.some((view) => view.type === 'panel') ?? false)\n ),\n 'An app cannot expose both an app view (`entry`) and panel views. Declare one or the other.',\n ),\n )\n .check(\n z.refine(\n (input) => (input.views?.filter((view) => view.type === 'panel').length ?? 0) <= 1,\n 'An app can expose at most one panel view.',\n ),\n )\n\n/** The `asset_source` variant of an app's `views`. @public */\nexport type AssetSourceView = Extract<\n NonNullable<z.output<typeof DefineAppInputSchema>['views']>[number],\n {type: 'asset_source'}\n>\n\n/** The `tile` variant of an app's `views`. @public */\nexport type TileView = Extract<\n NonNullable<z.output<typeof DefineAppInputSchema>['views']>[number],\n {type: 'tile'}\n>\n\n/**\n * User-facing input for `unstable_defineApp`. Excludes the internal\n * `applicationType`, `isSingleton`, and `config` — validated by the schema but\n * not part of the public surface (Sanity-owned apps set them via\n * `@ts-expect-error`). A union: an app declares an app `entry` (navigable) or\n * panel `views`, never both — but `asset_source` and `tile` views are separate\n * kinds and may accompany either.\n * @public\n */\nexport type DefineAppInput = Omit<\n z.output<typeof DefineAppInputSchema>,\n 'applicationType' | 'config' | 'entry' | 'isSingleton' | 'views'\n> &\n (\n | {entry?: never; views?: NonNullable<z.output<typeof DefineAppInputSchema>['views']>}\n | {entry?: string; views?: (AssetSourceView | TileView)[]}\n )\n\n/**\n * Nominal brand the CLI discriminates on to enable the workbench build/deploy\n * codepath. Registered via `Symbol.for` so the marker survives module-realm\n * boundaries — `@sanity/cli-core` re-derives the same global symbol with\n * `Symbol.for` rather than importing it, so it stays internal to this module.\n */\nconst WORKBENCH_APP: unique symbol = Symbol.for('sanity.workbench.defineApp')\n\n/**\n * The branded result of `unstable_defineApp`. Carries the same fields as the\n * input plus the internal brand — users only ever see `DefineAppInput`.\n * @public\n */\nexport type DefineAppResult = DefineAppInput & {readonly [WORKBENCH_APP]: true}\n\n/**\n * A branded app as the CLI reads it — the full schema shape, including the\n * internal fields `DefineAppInput` omits. Schema-derived so the narrowing\n * can't drift from what the schema validates.\n * @public\n */\nexport type WorkbenchApp = DefineAppResult & z.output<typeof DefineAppInputSchema>\n\n/**\n * Whether `app` is a branded `unstable_defineApp(...)` result — the sole\n * workbench opt-in.\n * @public\n */\nexport function isWorkbenchApp(app: unknown): app is WorkbenchApp {\n return typeof app === 'object' && app !== null && WORKBENCH_APP in app\n}\n\n/**\n * The app's config, or `undefined` when it declares none. Throws\n * when a non-singleton declares one — configs belong to Sanity-owned singletons,\n * so build/dev/deploy all read it through here to reject the combination\n * consistently.\n * @internal\n */\nexport function readConfig(app: WorkbenchApp): WorkbenchApp['config'] | undefined {\n if (app.config && !app.isSingleton) {\n throw new Error('`config` is only supported for singleton apps')\n }\n return app.config\n}\n\n/**\n * Declare a Sanity Workbench application. Identity at runtime — returns the same\n * object reference, tagged with the workbench brand. Field validation (the\n * `slug` pattern etc.) runs at build time in the CLI via `DefineAppInputSchema`;\n * this helper stays a thin, pure identity wrapper.\n * @public\n */\nexport function unstable_defineApp(input: DefineAppInput): DefineAppResult {\n return Object.defineProperty(input, WORKBENCH_APP, {\n configurable: false,\n enumerable: false,\n value: true,\n writable: false,\n }) as DefineAppResult\n}\n\n/**\n * One custom field a media library exposes. `src` default-exports a `defineField(...)` schema type.\n * @public\n */\nexport interface MediaLibraryField {\n /** Unique within the media library. */\n name: string\n src: string\n title: string\n\n /** Readable outside the owning organization. */\n public?: boolean\n}\n\n/**\n * Sanity-owned singleton, so authors don't name or title the app — only `organizationId` is required.\n * @public\n */\nexport interface DefineMediaLibraryInput {\n /** Organization that owns the media library — the CLI runs and deploys against it. */\n organizationId: string\n\n fields?: MediaLibraryField[]\n}\n\n/**\n * Declare the Sanity Media Library as a workbench app — a singleton whose `fields` become its config.\n * @public\n */\nexport function unstable_defineMediaLibrary(input: DefineMediaLibraryInput): DefineAppResult {\n return unstable_defineApp({\n // @ts-expect-error -- `applicationType`/`isSingleton`/`config` are internal, excluded from `DefineAppInput`; Sanity-owned apps set them\n applicationType: 'media-library',\n config: input.fields?.length ? {appType: 'media-library', fields: input.fields} : undefined,\n isSingleton: true,\n organizationId: input.organizationId,\n slug: 'media-library',\n title: 'Media Library',\n })\n}\n"],"names":["z","APP_SLUG_PATTERN","ConfigSchema","InterfaceDeclarationSchema","ServiceDeclarationSchema","APP_VISIBILITIES","ApplicationType","enum","DockGroupSchema","DefineAppInputSchema","object","applicationType","optional","config","entry","string","group","icon","isSingleton","boolean","organizationId","priority","number","services","array","check","refine","Set","map","service","name","size","length","slug","regex","title","views","view","visibility","input","undefined","error","path","some","type","filter","WORKBENCH_APP","Symbol","for","isWorkbenchApp","app","readConfig","Error","unstable_defineApp","Object","defineProperty","configurable","enumerable","value","writable","unstable_defineMediaLibrary","fields","appType"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/defineApp.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\nimport {APP_SLUG_PATTERN} from './appSlug.js'\nimport {DockSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema} from './contract.js'\n\n/**\n * Dashboard visibility values. Mirrors `APP_VISIBILITIES` in `@sanity/cli-core`\n * (which can't be imported here — pulling the barrel into this lean module bloats\n * the config-load path). Kept in sync by a type test in `defineApp.test.ts`.\n */\nconst APP_VISIBILITIES = ['default', 'unlisted', 'disabled'] as const\n\n/**\n * Internal application discriminator. Sanity-owned singleton apps only;\n * validated by the schema but excluded from the public `DefineAppInput` type.\n */\nconst ApplicationType = z.enum(['coreApp', 'studio', 'canvas', 'dashboard'])\n\n/**\n * Runtime-validation schema for `defineApplication`.\n * @internal\n */\nexport const DefineAppInputSchema = z\n .object({\n /**\n * Internal — Sanity-owned singleton apps only. Validated here but excluded\n * from the public `DefineAppInput` type.\n * @internal\n */\n applicationType: z.optional(ApplicationType),\n /** Default placement inherited by panel and window views. */\n dock: z.optional(DockSchema),\n /**\n * App entrypoint module. Defaults to `./src/App.tsx` when omitted. The build\n * derives the app's navigable `app` view from it. SDK apps only — setting it\n * on a studio is rejected (studio app views are not yet implemented).\n */\n entry: z.optional(z.string(\"must be a path to the app's entry file\")),\n /** Optional icon override (path to an SVG). Wins over manifest/studio icon. */\n icon: z.optional(z.string()),\n /**\n * Stable identity, distinct from the `slug` address. Defaults to `slug` when\n * omitted. Identity keys — the build id and derived interface ids — are built\n * from this, so renaming the slug no longer changes identity.\n */\n name: z.optional(\n z\n .string()\n .check(\n z.regex(\n APP_SLUG_PATTERN,\n 'App `name` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric',\n ),\n ),\n ),\n /** Organization that owns the app — the workbench runs and deploys against it. */\n organizationId: z.string(\n \"App `organizationId` is required — pass the owning organization's ID to `defineApplication`\",\n ),\n slug: z\n .string('App `slug` is required — the hostname the application is created at on deploy')\n .check(\n z.regex(\n APP_SLUG_PATTERN,\n 'App `slug` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric',\n ),\n ),\n /** User-facing app title. Wins over studio.config.ts title on merge. */\n title: z.string(),\n /** Views the app exposes (e.g. a window, dock panels, and tiles). */\n views: z.optional(z.array(InterfaceDeclarationSchema, 'must be an array of views')),\n /** Dashboard visibility of the app. Defaults to `default` when omitted. */\n visibility: z.optional(z.enum(APP_VISIBILITIES)),\n /** Background web workers the app runs. */\n webWorkers: z.optional(z.array(ServiceDeclarationSchema, 'must be an array of web workers')),\n })\n .check(\n z.refine((input) => {\n const names = [\n ...(input.entry === undefined ? [] : [input.name ?? input.slug]),\n ...(input.views ?? []).map((view) => view.name),\n ...(input.webWorkers ?? []).map((webWorker) => webWorker.name),\n ]\n return new Set(names).size === names.length\n }, '`name` must be unique across views and web workers. Rename one of the duplicates.'),\n // Studio app views are not implemented yet. A studio that declares `entry`\n // (the SDK app-view entrypoint) is rejected here rather than silently\n // generating one; studios keep navigating via their existing render path.\n z.refine((input) => !(input.applicationType === 'studio' && input.entry !== undefined), {\n error: 'App views for studios are not implemented yet',\n path: ['entry'],\n }),\n )\n\n/**\n * User-facing input for `defineApplication`. Excludes the internal\n * `applicationType`.\n * @public\n */\nexport type DefineAppInput = Omit<z.output<typeof DefineAppInputSchema>, 'applicationType'>\n\n/**\n * Nominal brand the CLI discriminates on to enable the workbench build/deploy\n * codepath. Registered via `Symbol.for` so the marker survives module-realm\n * boundaries — `@sanity/cli-core` re-derives the same global symbol with\n * `Symbol.for` rather than importing it, so it stays internal to this module.\n */\nconst WORKBENCH_APP: unique symbol = Symbol.for('sanity.workbench.defineApp')\n\n/**\n * The branded result of `defineApplication`. Carries the same fields as the\n * input plus the internal brand — users only ever see `DefineAppInput`.\n * @public\n */\nexport type DefineAppResult = DefineAppInput & {readonly [WORKBENCH_APP]: true}\n\n/**\n * A branded app as the CLI reads it — the full schema shape, including the\n * internal fields `DefineAppInput` omits. Schema-derived so the narrowing\n * can't drift from what the schema validates.\n * @public\n */\nexport type WorkbenchApp = DefineAppResult & z.output<typeof DefineAppInputSchema>\n\n/**\n * Whether `app` is a branded `defineApplication(...)` result — the sole\n * workbench opt-in.\n * @public\n */\nexport function isWorkbenchApp(app: unknown): app is WorkbenchApp {\n return typeof app === 'object' && app !== null && WORKBENCH_APP in app\n}\n\n/**\n * Declare a Sanity Workbench application. Identity at runtime — returns the same\n * object reference, tagged with the workbench brand. Field validation (the\n * `slug` pattern etc.) runs at build time in the CLI via `DefineAppInputSchema`;\n * this helper stays a thin, pure identity wrapper.\n * @public\n */\nexport function defineApplication(input: DefineAppInput): DefineAppResult {\n return Object.defineProperty(input, WORKBENCH_APP, {\n configurable: false,\n enumerable: false,\n value: true,\n writable: false,\n }) as DefineAppResult\n}\n\n/**\n * One custom field a media library exposes. `src` default-exports a `defineField(...)` schema type.\n * @public\n */\nexport interface MediaLibraryField {\n /** Unique within the media library. */\n name: string\n src: string\n title: string\n\n /** Readable outside the owning organization. */\n public?: boolean\n}\n\n/**\n * Sanity-owned singleton, so authors don't name or title the app — only `organizationId` is required.\n * @public\n */\nexport interface DefineMediaLibraryInput {\n /** Organization that owns the media library — the CLI runs and deploys against it. */\n organizationId: string\n\n fields?: MediaLibraryField[]\n}\n\n/**\n * Nominal brand the CLI discriminates a config on. A config is *not* an app: it\n * carries no `slug`/`title`/application identity, only a target `appType`, its\n * owning `organizationId`, and its `fields`. Registered via `Symbol.for` so the\n * marker survives module-realm boundaries — `@sanity/cli-core` re-derives the\n * same global symbol rather than importing it.\n */\nconst WORKBENCH_CONFIG: unique symbol = Symbol.for('sanity.workbench.defineConfig')\n\n/**\n * A branded workbench config as the CLI reads it: bound to its target app by\n * `appType`, not carrying its own app identity. The deploy/undeploy/build paths\n * discriminate it via {@link isWorkbenchConfig}.\n * @public\n */\nexport interface WorkbenchConfig {\n appType: 'media-library'\n fields: MediaLibraryField[]\n organizationId: string\n readonly [WORKBENCH_CONFIG]: true\n}\n\n/**\n * Whether `app` is a branded `unstable_defineMediaLibrary(...)` config — a\n * config-carrier rather than an app. Configs live in `cliConfig.app` alongside\n * apps but resolve through `resolveWorkbenchConfig`, not `resolveWorkbenchApp`.\n * @public\n */\nexport function isWorkbenchConfig(app: unknown): app is WorkbenchConfig {\n return typeof app === 'object' && app !== null && WORKBENCH_CONFIG in app\n}\n\n/**\n * Declare the Sanity Media Library config. The Media Library is a Sanity-owned\n * singleton, so this is not an app — it carries no slug or title, only the\n * target `appType`, its owning organization, and the `fields` it contributes to\n * the installation's config.\n * @public\n */\nexport function unstable_defineMediaLibrary(input: DefineMediaLibraryInput): WorkbenchConfig {\n return Object.defineProperty(\n {appType: 'media-library', fields: input.fields ?? [], organizationId: input.organizationId},\n WORKBENCH_CONFIG,\n {configurable: false, enumerable: false, value: true, writable: false},\n ) as WorkbenchConfig\n}\n"],"names":["z","APP_SLUG_PATTERN","DockSchema","InterfaceDeclarationSchema","ServiceDeclarationSchema","APP_VISIBILITIES","ApplicationType","enum","DefineAppInputSchema","object","applicationType","optional","dock","entry","string","icon","name","check","regex","organizationId","slug","title","views","array","visibility","webWorkers","refine","input","names","undefined","map","view","webWorker","Set","size","length","error","path","WORKBENCH_APP","Symbol","for","isWorkbenchApp","app","defineApplication","Object","defineProperty","configurable","enumerable","value","writable","WORKBENCH_CONFIG","isWorkbenchConfig","unstable_defineMediaLibrary","appType","fields"],"mappings":"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"}
|
package/dist/defineView.js
CHANGED
|
@@ -4,14 +4,14 @@ import { VIEW_CONTRACT_VERSION } from './contract.js';
|
|
|
4
4
|
* and the props each component receives — `"panel"` yields a `{title, panel}`
|
|
5
5
|
* record whose components are typed with the panel props.
|
|
6
6
|
*
|
|
7
|
-
* Returns the component(s) tagged with their
|
|
7
|
+
* Returns the component(s) tagged with their surface and contract version, for
|
|
8
8
|
* the CLI build to generate render artifacts from. Used as the default export of
|
|
9
9
|
* a view's `src` file.
|
|
10
10
|
* @public
|
|
11
|
-
*/ export function unstable_defineView(
|
|
11
|
+
*/ export function unstable_defineView(surface, components) {
|
|
12
12
|
return {
|
|
13
13
|
components,
|
|
14
|
-
|
|
14
|
+
surface,
|
|
15
15
|
version: VIEW_CONTRACT_VERSION
|
|
16
16
|
};
|
|
17
17
|
}
|
package/dist/defineView.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/defineView.ts"],"sourcesContent":["import {type AssetSourceComponentProps} from '@sanity/types'\n\nimport {\n type
|
|
1
|
+
{"version":3,"sources":["../src/defineView.ts"],"sourcesContent":["import {type AssetSourceComponentProps} from '@sanity/types'\n\nimport {\n type TileSize,\n VIEW_CONTRACT_VERSION,\n type ViewComponent,\n type ViewComponentBaseProps,\n type ViewSurface,\n} from './contract.js'\n\n/** @public */\nexport type WindowViewProps = ViewComponentBaseProps<{\n name: string\n src: string\n surface: 'app'\n title: string\n}>\n\n/**\n * Props a panel component receives: its interface record, minus the\n * service-assigned `id`/`deployment_id` a local dev server can't provide. Mirrors\n * the `panel` record the workbench host renders from (the wire format owned by\n * `@sanity/workbench`); drift is guarded by the stamped contract version.\n * @public\n */\nexport type PanelViewProps = ViewComponentBaseProps<{\n name: string\n src: string\n surface: 'panel'\n title: string\n}>\n\n/**\n * The component slots a `panel` view exposes — each its own module-federation\n * island, typed with the panel props.\n * @public\n */\nexport interface PanelViewComponents {\n panel: ViewComponent<PanelViewProps>\n title: ViewComponent<PanelViewProps>\n}\n\n/**\n * A panel's view-component slot — the module-federation expose for one island.\n * @public\n */\nexport type PanelComponent = keyof PanelViewComponents\n\n/**\n * The component slots an `asset_source` view exposes — a single picker island,\n * typed with the studio asset-source props it renders behind. The props are\n * `@sanity/types`' `AssetSourceComponentProps` directly, so an authored picker\n * receives exactly what a studio `AssetSource.component` does.\n * @public\n */\nexport interface AssetSourceViewComponents {\n asset_source: ViewComponent<AssetSourceComponentProps>\n}\n\n/**\n * An asset source's view-component slot — the module-federation expose for its\n * one island.\n * @public\n */\nexport type AssetSourceComponent = keyof AssetSourceViewComponents\n\n/**\n * Props a tile component receives: its own interface record plus its footprint\n * `size`, so it can render per family. Mirrors the\n * `tile` record the dashboard host renders from; drift is guarded by the stamped\n * contract version. Placement `order` is host-only metadata, not surfaced here.\n * @public\n */\nexport type TileViewProps = ViewComponentBaseProps<{\n name: string\n size: TileSize\n src: string\n surface: 'tile'\n title: string\n}>\n\n/**\n * The component slot a `tile` view exposes — a single island, typed with the\n * tile props.\n * @public\n */\nexport interface TileViewComponents {\n tile: ViewComponent<TileViewProps>\n}\n\n/**\n * A tile's view-component slot — the module-federation expose for its one island.\n * @public\n */\nexport type TileComponent = keyof TileViewComponents\n\n/**\n * The components each view surface exposes.\n * @public\n */\nexport interface ViewComponentsBySurface {\n app: ViewComponent<WindowViewProps>\n asset_source: AssetSourceViewComponents\n panel: PanelViewComponents\n tile: TileViewComponents\n}\n\n/**\n * The result of `unstable_defineView`: the author's component(s), the view surface,\n * and the internal contract version the build artifact targets.\n * @public\n */\nexport interface DefinedView<TSurface extends ViewSurface = ViewSurface> {\n readonly components: ViewComponentsBySurface[TSurface]\n readonly surface: TSurface\n /** @internal */\n readonly version: typeof VIEW_CONTRACT_VERSION\n}\n\n/**\n * Define a Sanity Workbench view. The first argument narrows the component shape\n * and the props each component receives — `\"panel\"` yields a `{title, panel}`\n * record whose components are typed with the panel props.\n *\n * Returns the component(s) tagged with their surface and contract version, for\n * the CLI build to generate render artifacts from. Used as the default export of\n * a view's `src` file.\n * @public\n */\nexport function unstable_defineView<TSurface extends ViewSurface>(\n surface: TSurface,\n components: ViewComponentsBySurface[TSurface],\n): DefinedView<TSurface> {\n return {components, surface, version: VIEW_CONTRACT_VERSION}\n}\n"],"names":["VIEW_CONTRACT_VERSION","unstable_defineView","surface","components","version"],"mappings":"AAEA,SAEEA,qBAAqB,QAIhB,gBAAe;AA+GtB;;;;;;;;;CASC,GACD,OAAO,SAASC,oBACdC,OAAiB,EACjBC,UAA6C;IAE7C,OAAO;QAACA;QAAYD;QAASE,SAASJ;IAAqB;AAC7D"}
|
package/dist/deriveInterfaces.js
CHANGED
|
@@ -14,47 +14,73 @@ import { isWorkbenchApp } from './defineApp.js';
|
|
|
14
14
|
throw new Error('App views for studios are not implemented yet');
|
|
15
15
|
}
|
|
16
16
|
const entry = isApp ? app.entry : GENERATED_ENTRY;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
// Identity, not address: interface ids and the app-view name key on `name`
|
|
18
|
+
// (which defaults to `slug`, so existing apps derive byte-identical ids).
|
|
19
|
+
const appName = app.name ?? app.slug;
|
|
20
|
+
const shared = (kind, declaration)=>({
|
|
21
|
+
id: `${appName}-${kind}-${declaration.name}`,
|
|
22
|
+
moduleId: interfaceModuleId(kind, declaration.name),
|
|
20
23
|
name: declaration.name,
|
|
21
24
|
src: declaration.src,
|
|
22
25
|
title: declaration.title,
|
|
23
|
-
|
|
24
|
-
version: interfaceContractVersion(type)
|
|
26
|
+
version: interfaceContractVersion(kind)
|
|
25
27
|
});
|
|
28
|
+
const placementMetadata = (dock)=>{
|
|
29
|
+
const group = dock?.group ?? app.dock?.group;
|
|
30
|
+
const order = dock?.order ?? app.dock?.order;
|
|
31
|
+
if (group === undefined && order === undefined) return null;
|
|
32
|
+
return {
|
|
33
|
+
dock: {
|
|
34
|
+
...group === undefined ? {} : {
|
|
35
|
+
group
|
|
36
|
+
},
|
|
37
|
+
...order === undefined ? {} : {
|
|
38
|
+
order
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
};
|
|
26
43
|
return [
|
|
27
44
|
...(app.views ?? []).map((view)=>{
|
|
28
|
-
|
|
29
|
-
// (required) and an optional sort `priority`. Both authored top-level.
|
|
30
|
-
if (view.type === 'tile') {
|
|
45
|
+
if (view.surface === 'tile') {
|
|
31
46
|
return {
|
|
32
|
-
...shared(view.
|
|
33
|
-
metadata: view.
|
|
47
|
+
...shared(view.surface, view),
|
|
48
|
+
metadata: view.order === undefined ? {
|
|
34
49
|
size: view.size
|
|
35
50
|
} : {
|
|
36
|
-
|
|
51
|
+
order: view.order,
|
|
37
52
|
size: view.size
|
|
38
|
-
}
|
|
53
|
+
},
|
|
54
|
+
surface: view.surface
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (view.surface === 'app' || view.surface === 'panel') {
|
|
58
|
+
return {
|
|
59
|
+
...shared(view.surface, view),
|
|
60
|
+
metadata: placementMetadata(view.dock),
|
|
61
|
+
surface: view.surface
|
|
39
62
|
};
|
|
40
63
|
}
|
|
41
64
|
return {
|
|
42
|
-
...shared(view.
|
|
43
|
-
metadata: null
|
|
65
|
+
...shared(view.surface, view),
|
|
66
|
+
metadata: null,
|
|
67
|
+
surface: view.surface
|
|
44
68
|
};
|
|
45
69
|
}),
|
|
46
|
-
...(app.
|
|
47
|
-
...shared('worker',
|
|
48
|
-
metadata: null
|
|
70
|
+
...(app.webWorkers ?? []).map((webWorker)=>({
|
|
71
|
+
...shared('worker', webWorker),
|
|
72
|
+
metadata: null,
|
|
73
|
+
type: 'worker'
|
|
49
74
|
})),
|
|
50
75
|
...entry === undefined ? [] : [
|
|
51
76
|
{
|
|
52
77
|
...shared('app', {
|
|
53
|
-
name:
|
|
78
|
+
name: appName,
|
|
54
79
|
src: entry,
|
|
55
80
|
title: appTitle ?? app.title
|
|
56
81
|
}),
|
|
57
|
-
metadata:
|
|
82
|
+
metadata: placementMetadata(),
|
|
83
|
+
surface: 'app'
|
|
58
84
|
}
|
|
59
85
|
]
|
|
60
86
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/deriveInterfaces.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\n\nimport {FEDERATION_FILE_NAME, RUNTIME_DIR} from './actions/build/vite/constants.js'\nimport {\n type
|
|
1
|
+
{"version":3,"sources":["../src/deriveInterfaces.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\n\nimport {FEDERATION_FILE_NAME, RUNTIME_DIR} from './actions/build/vite/constants.js'\nimport {\n type Dock,\n interfaceContractVersion,\n type InterfaceKind,\n interfaceModuleId,\n type TileInterfaceMetadata,\n type ViewPlacementMetadata,\n} from './contract.js'\nimport {isWorkbenchApp} from './defineApp.js'\n\n/** What a studio exposes as `./App` — the generated entry that renders `sanity.config.ts`. */\nconst GENERATED_ENTRY = `./${RUNTIME_DIR}/${FEDERATION_FILE_NAME}.jsx`\n\ninterface DerivedInterfaceBase {\n /** CLI-minted for a local interface; a deployed one gets its id from Brett. */\n id: string\n moduleId: string\n name: string\n src: string\n title: string\n\n version?: string\n}\n\n/** @internal */\nexport type DerivedInterface =\n | (DerivedInterfaceBase & {metadata: null; surface: 'asset_source'})\n | (DerivedInterfaceBase & {metadata: null; type: 'worker'})\n | (DerivedInterfaceBase & {metadata: TileInterfaceMetadata; surface: 'tile'})\n | (DerivedInterfaceBase & {metadata: ViewPlacementMetadata | null; surface: 'app'})\n | (DerivedInterfaceBase & {metadata: ViewPlacementMetadata | null; surface: 'panel'})\n\n/**\n * `appTitle` titles the app view where a deploy resolved one through `--title`;\n * dev takes it from the config.\n * @internal\n */\nexport function deriveInterfaces(\n app: CliConfig['app'],\n {appTitle, isApp}: {appTitle?: string; isApp: boolean},\n): DerivedInterface[] {\n if (!isWorkbenchApp(app)) return []\n\n // The schema rejects a *declared* studio with an `entry`; `isApp` is resolved\n // from the project, so a detected one only fails here.\n if (!isApp && app.entry !== undefined) {\n throw new Error('App views for studios are not implemented yet')\n }\n const entry = isApp ? app.entry : GENERATED_ENTRY\n\n // Identity, not address: interface ids and the app-view name key on `name`\n // (which defaults to `slug`, so existing apps derive byte-identical ids).\n const appName = app.name ?? app.slug\n\n const shared = (\n kind: InterfaceKind,\n declaration: {name: string; src: string; title: string},\n ) => ({\n id: `${appName}-${kind}-${declaration.name}`,\n moduleId: interfaceModuleId(kind, declaration.name),\n name: declaration.name,\n src: declaration.src,\n title: declaration.title,\n version: interfaceContractVersion(kind),\n })\n\n const placementMetadata = (dock?: Dock): ViewPlacementMetadata | null => {\n const group = dock?.group ?? app.dock?.group\n const order = dock?.order ?? app.dock?.order\n if (group === undefined && order === undefined) return null\n return {\n dock: {\n ...(group === undefined ? {} : {group}),\n ...(order === undefined ? {} : {order}),\n },\n }\n }\n\n return [\n ...(app.views ?? []).map((view): DerivedInterface => {\n if (view.surface === 'tile') {\n return {\n ...shared(view.surface, view),\n metadata:\n view.order === undefined ? {size: view.size} : {order: view.order, size: view.size},\n surface: view.surface,\n }\n }\n if (view.surface === 'app' || view.surface === 'panel') {\n return {\n ...shared(view.surface, view),\n metadata: placementMetadata(view.dock),\n surface: view.surface,\n }\n }\n return {...shared(view.surface, view), metadata: null, surface: view.surface}\n }),\n ...(app.webWorkers ?? []).map(\n (webWorker): DerivedInterface => ({\n ...shared('worker', webWorker),\n metadata: null,\n type: 'worker',\n }),\n ),\n ...(entry === undefined\n ? []\n : [\n {\n ...shared('app', {name: appName, src: entry, title: appTitle ?? app.title}),\n metadata: placementMetadata(),\n surface: 'app' as const,\n },\n ]),\n ]\n}\n"],"names":["FEDERATION_FILE_NAME","RUNTIME_DIR","interfaceContractVersion","interfaceModuleId","isWorkbenchApp","GENERATED_ENTRY","deriveInterfaces","app","appTitle","isApp","entry","undefined","Error","appName","name","slug","shared","kind","declaration","id","moduleId","src","title","version","placementMetadata","dock","group","order","views","map","view","surface","metadata","size","webWorkers","webWorker","type"],"mappings":"AAEA,SAAQA,oBAAoB,EAAEC,WAAW,QAAO,oCAAmC;AACnF,SAEEC,wBAAwB,EAExBC,iBAAiB,QAGZ,gBAAe;AACtB,SAAQC,cAAc,QAAO,iBAAgB;AAE7C,4FAA4F,GAC5F,MAAMC,kBAAkB,CAAC,EAAE,EAAEJ,YAAY,CAAC,EAAED,qBAAqB,IAAI,CAAC;AAqBtE;;;;CAIC,GACD,OAAO,SAASM,iBACdC,GAAqB,EACrB,EAACC,QAAQ,EAAEC,KAAK,EAAsC;IAEtD,IAAI,CAACL,eAAeG,MAAM,OAAO,EAAE;IAEnC,8EAA8E;IAC9E,uDAAuD;IACvD,IAAI,CAACE,SAASF,IAAIG,KAAK,KAAKC,WAAW;QACrC,MAAM,IAAIC,MAAM;IAClB;IACA,MAAMF,QAAQD,QAAQF,IAAIG,KAAK,GAAGL;IAElC,2EAA2E;IAC3E,0EAA0E;IAC1E,MAAMQ,UAAUN,IAAIO,IAAI,IAAIP,IAAIQ,IAAI;IAEpC,MAAMC,SAAS,CACbC,MACAC,cACI,CAAA;YACJC,IAAI,GAAGN,QAAQ,CAAC,EAAEI,KAAK,CAAC,EAAEC,YAAYJ,IAAI,EAAE;YAC5CM,UAAUjB,kBAAkBc,MAAMC,YAAYJ,IAAI;YAClDA,MAAMI,YAAYJ,IAAI;YACtBO,KAAKH,YAAYG,GAAG;YACpBC,OAAOJ,YAAYI,KAAK;YACxBC,SAASrB,yBAAyBe;QACpC,CAAA;IAEA,MAAMO,oBAAoB,CAACC;QACzB,MAAMC,QAAQD,MAAMC,SAASnB,IAAIkB,IAAI,EAAEC;QACvC,MAAMC,QAAQF,MAAME,SAASpB,IAAIkB,IAAI,EAAEE;QACvC,IAAID,UAAUf,aAAagB,UAAUhB,WAAW,OAAO;QACvD,OAAO;YACLc,MAAM;gBACJ,GAAIC,UAAUf,YAAY,CAAC,IAAI;oBAACe;gBAAK,CAAC;gBACtC,GAAIC,UAAUhB,YAAY,CAAC,IAAI;oBAACgB;gBAAK,CAAC;YACxC;QACF;IACF;IAEA,OAAO;WACF,AAACpB,CAAAA,IAAIqB,KAAK,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC;YACxB,IAAIA,KAAKC,OAAO,KAAK,QAAQ;gBAC3B,OAAO;oBACL,GAAGf,OAAOc,KAAKC,OAAO,EAAED,KAAK;oBAC7BE,UACEF,KAAKH,KAAK,KAAKhB,YAAY;wBAACsB,MAAMH,KAAKG,IAAI;oBAAA,IAAI;wBAACN,OAAOG,KAAKH,KAAK;wBAAEM,MAAMH,KAAKG,IAAI;oBAAA;oBACpFF,SAASD,KAAKC,OAAO;gBACvB;YACF;YACA,IAAID,KAAKC,OAAO,KAAK,SAASD,KAAKC,OAAO,KAAK,SAAS;gBACtD,OAAO;oBACL,GAAGf,OAAOc,KAAKC,OAAO,EAAED,KAAK;oBAC7BE,UAAUR,kBAAkBM,KAAKL,IAAI;oBACrCM,SAASD,KAAKC,OAAO;gBACvB;YACF;YACA,OAAO;gBAAC,GAAGf,OAAOc,KAAKC,OAAO,EAAED,KAAK;gBAAEE,UAAU;gBAAMD,SAASD,KAAKC,OAAO;YAAA;QAC9E;WACG,AAACxB,CAAAA,IAAI2B,UAAU,IAAI,EAAE,AAAD,EAAGL,GAAG,CAC3B,CAACM,YAAiC,CAAA;gBAChC,GAAGnB,OAAO,UAAUmB,UAAU;gBAC9BH,UAAU;gBACVI,MAAM;YACR,CAAA;WAEE1B,UAAUC,YACV,EAAE,GACF;YACE;gBACE,GAAGK,OAAO,OAAO;oBAACF,MAAMD;oBAASQ,KAAKX;oBAAOY,OAAOd,YAAYD,IAAIe,KAAK;gBAAA,EAAE;gBAC3EU,UAAUR;gBACVO,SAAS;YACX;SACD;KACN;AACH"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
// Keep this module browser-safe: Vite loads it directly in the generated dashboard runtime.
|
|
3
|
+
import { createInstance } from '@module-federation/runtime';
|
|
4
|
+
import { BehaviorSubject } from 'rxjs';
|
|
5
|
+
const remoteName = 'workbench-remote';
|
|
6
|
+
const remoteModuleId = `${remoteName}/App`;
|
|
7
|
+
const localApplicationsEvent = 'sanity:workbench:local-applications';
|
|
8
|
+
const noop = ()=>{};
|
|
9
|
+
export async function renderDashboard(rootElement, config, options) {
|
|
10
|
+
if (!rootElement) {
|
|
11
|
+
throw new Error('Missing root element to mount application into');
|
|
12
|
+
}
|
|
13
|
+
const deployedDashboardHost = globalThis.__SANITY_STAGING__ === true ? 'workbench-apps-of5p8qpku.run.sanity.work' : 'workbench-apps-osyh1iet5.sanity.run';
|
|
14
|
+
const remoteUrl = import.meta.env.SANITY_INTERNAL_WORKBENCH_REMOTE_URL ?? `https://${deployedDashboardHost}/mf-manifest.json`;
|
|
15
|
+
const federation = createInstance({
|
|
16
|
+
name: 'sanity-workbench',
|
|
17
|
+
remotes: [
|
|
18
|
+
{
|
|
19
|
+
entry: remoteUrl,
|
|
20
|
+
name: remoteName
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
});
|
|
24
|
+
let remoteModule;
|
|
25
|
+
try {
|
|
26
|
+
remoteModule = await federation.loadRemote(remoteModuleId);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new Error(`Failed to load remote module "${remoteModuleId}"`, {
|
|
29
|
+
cause: error
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (!remoteModule || typeof remoteModule.render !== 'function') {
|
|
33
|
+
throw new Error(`Remote module "${remoteModuleId}" did not expose a render function`);
|
|
34
|
+
}
|
|
35
|
+
let appConfigs;
|
|
36
|
+
let cleanupHmr = noop;
|
|
37
|
+
let localApplications;
|
|
38
|
+
if (import.meta.hot) {
|
|
39
|
+
const hot = import.meta.hot;
|
|
40
|
+
const hmrLocalApplications = new BehaviorSubject([]);
|
|
41
|
+
const hmrAppConfigs = new BehaviorSubject([]);
|
|
42
|
+
const handler = (payload)=>{
|
|
43
|
+
hmrLocalApplications.next(payload.applications);
|
|
44
|
+
hmrAppConfigs.next(payload.configs ?? []);
|
|
45
|
+
};
|
|
46
|
+
localApplications = hmrLocalApplications;
|
|
47
|
+
appConfigs = hmrAppConfigs;
|
|
48
|
+
hot.on(localApplicationsEvent, handler);
|
|
49
|
+
hot.send('sanity:workbench:get-local-applications');
|
|
50
|
+
cleanupHmr = ()=>hot.off(localApplicationsEvent, handler);
|
|
51
|
+
}
|
|
52
|
+
const unmount = remoteModule.render(rootElement, {
|
|
53
|
+
appConfigs,
|
|
54
|
+
config,
|
|
55
|
+
localApplications
|
|
56
|
+
}, options);
|
|
57
|
+
return ()=>{
|
|
58
|
+
cleanupHmr();
|
|
59
|
+
unmount();
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=renderDashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/renderDashboard.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\n\n// Keep this module browser-safe: Vite loads it directly in the generated dashboard runtime.\nimport {createInstance} from '@module-federation/runtime'\nimport {BehaviorSubject, type Observable} from 'rxjs'\n\ndeclare global {\n var __SANITY_STAGING__: boolean | undefined\n}\n\ninterface DashboardConfig {\n organizationId: string\n}\n\ninterface RenderDashboardOptions {\n reactStrictMode?: boolean\n}\n\ninterface DashboardRemoteModule {\n render: (\n rootElement: HTMLElement,\n props: {\n appConfigs?: Observable<unknown[]>\n config: DashboardConfig\n localApplications?: Observable<unknown[]>\n },\n options?: RenderDashboardOptions,\n ) => () => void\n}\n\nconst remoteName = 'workbench-remote'\nconst remoteModuleId = `${remoteName}/App`\nconst localApplicationsEvent = 'sanity:workbench:local-applications'\nconst noop = () => {}\n\nexport async function renderDashboard(\n rootElement: HTMLElement | null,\n config: DashboardConfig,\n options?: RenderDashboardOptions,\n): Promise<() => void> {\n if (!rootElement) {\n throw new Error('Missing root element to mount application into')\n }\n\n const deployedDashboardHost =\n globalThis.__SANITY_STAGING__ === true\n ? 'workbench-apps-of5p8qpku.run.sanity.work'\n : 'workbench-apps-osyh1iet5.sanity.run'\n const remoteUrl =\n import.meta.env.SANITY_INTERNAL_WORKBENCH_REMOTE_URL ??\n `https://${deployedDashboardHost}/mf-manifest.json`\n\n const federation = createInstance({\n name: 'sanity-workbench',\n remotes: [{entry: remoteUrl, name: remoteName}],\n })\n\n let remoteModule: DashboardRemoteModule | null\n try {\n remoteModule = await federation.loadRemote<DashboardRemoteModule>(remoteModuleId)\n } catch (error) {\n throw new Error(`Failed to load remote module \"${remoteModuleId}\"`, {cause: error})\n }\n\n if (!remoteModule || typeof remoteModule.render !== 'function') {\n throw new Error(`Remote module \"${remoteModuleId}\" did not expose a render function`)\n }\n\n let appConfigs: BehaviorSubject<unknown[]> | undefined\n let cleanupHmr = noop\n let localApplications: BehaviorSubject<unknown[]> | undefined\n\n if (import.meta.hot) {\n const hot = import.meta.hot\n const hmrLocalApplications = new BehaviorSubject<unknown[]>([])\n const hmrAppConfigs = new BehaviorSubject<unknown[]>([])\n const handler = (payload: {applications: unknown[]; configs?: unknown[]}) => {\n hmrLocalApplications.next(payload.applications)\n hmrAppConfigs.next(payload.configs ?? [])\n }\n\n localApplications = hmrLocalApplications\n appConfigs = hmrAppConfigs\n hot.on(localApplicationsEvent, handler)\n hot.send('sanity:workbench:get-local-applications')\n cleanupHmr = () => hot.off(localApplicationsEvent, handler)\n }\n\n const unmount = remoteModule.render(rootElement, {appConfigs, config, localApplications}, options)\n\n return () => {\n cleanupHmr()\n unmount()\n }\n}\n"],"names":["createInstance","BehaviorSubject","remoteName","remoteModuleId","localApplicationsEvent","noop","renderDashboard","rootElement","config","options","Error","deployedDashboardHost","globalThis","__SANITY_STAGING__","remoteUrl","env","SANITY_INTERNAL_WORKBENCH_REMOTE_URL","federation","name","remotes","entry","remoteModule","loadRemote","error","cause","render","appConfigs","cleanupHmr","localApplications","hot","hmrLocalApplications","hmrAppConfigs","handler","payload","next","applications","configs","on","send","off","unmount"],"mappings":"AAAA,qCAAqC;AAErC,4FAA4F;AAC5F,SAAQA,cAAc,QAAO,6BAA4B;AACzD,SAAQC,eAAe,QAAwB,OAAM;AA0BrD,MAAMC,aAAa;AACnB,MAAMC,iBAAiB,GAAGD,WAAW,IAAI,CAAC;AAC1C,MAAME,yBAAyB;AAC/B,MAAMC,OAAO,KAAO;AAEpB,OAAO,eAAeC,gBACpBC,WAA+B,EAC/BC,MAAuB,EACvBC,OAAgC;IAEhC,IAAI,CAACF,aAAa;QAChB,MAAM,IAAIG,MAAM;IAClB;IAEA,MAAMC,wBACJC,WAAWC,kBAAkB,KAAK,OAC9B,6CACA;IACN,MAAMC,YACJ,YAAYC,GAAG,CAACC,oCAAoC,IACpD,CAAC,QAAQ,EAAEL,sBAAsB,iBAAiB,CAAC;IAErD,MAAMM,aAAajB,eAAe;QAChCkB,MAAM;QACNC,SAAS;YAAC;gBAACC,OAAON;gBAAWI,MAAMhB;YAAU;SAAE;IACjD;IAEA,IAAImB;IACJ,IAAI;QACFA,eAAe,MAAMJ,WAAWK,UAAU,CAAwBnB;IACpE,EAAE,OAAOoB,OAAO;QACd,MAAM,IAAIb,MAAM,CAAC,8BAA8B,EAAEP,eAAe,CAAC,CAAC,EAAE;YAACqB,OAAOD;QAAK;IACnF;IAEA,IAAI,CAACF,gBAAgB,OAAOA,aAAaI,MAAM,KAAK,YAAY;QAC9D,MAAM,IAAIf,MAAM,CAAC,eAAe,EAAEP,eAAe,kCAAkC,CAAC;IACtF;IAEA,IAAIuB;IACJ,IAAIC,aAAatB;IACjB,IAAIuB;IAEJ,IAAI,YAAYC,GAAG,EAAE;QACnB,MAAMA,MAAM,YAAYA,GAAG;QAC3B,MAAMC,uBAAuB,IAAI7B,gBAA2B,EAAE;QAC9D,MAAM8B,gBAAgB,IAAI9B,gBAA2B,EAAE;QACvD,MAAM+B,UAAU,CAACC;YACfH,qBAAqBI,IAAI,CAACD,QAAQE,YAAY;YAC9CJ,cAAcG,IAAI,CAACD,QAAQG,OAAO,IAAI,EAAE;QAC1C;QAEAR,oBAAoBE;QACpBJ,aAAaK;QACbF,IAAIQ,EAAE,CAACjC,wBAAwB4B;QAC/BH,IAAIS,IAAI,CAAC;QACTX,aAAa,IAAME,IAAIU,GAAG,CAACnC,wBAAwB4B;IACrD;IAEA,MAAMQ,UAAUnB,aAAaI,MAAM,CAAClB,aAAa;QAACmB;QAAYlB;QAAQoB;IAAiB,GAAGnB;IAE1F,OAAO;QACLkB;QACAa;IACF;AACF"}
|
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
// Package-internal shared resolver: turn a CLI config's branded
|
|
2
|
-
// `
|
|
3
|
-
// project
|
|
4
|
-
//
|
|
2
|
+
// `defineApplication` app into its declared interfaces, or `null` for a plain
|
|
3
|
+
// project — and for a config (a branded `unstable_defineMediaLibrary`), which
|
|
4
|
+
// is not an app. The build and deploy accessors (actions/build, actions/deploy)
|
|
5
|
+
// each build their command-specific view on top of this one brand-check +
|
|
5
6
|
// extraction, so the discrimination lives in exactly one place.
|
|
6
|
-
import { isWorkbenchApp
|
|
7
|
+
import { isWorkbenchApp } from './defineApp.js';
|
|
7
8
|
import { formatWorkbenchAppErrors, validateWorkbenchApp } from './validateWorkbenchApp.js';
|
|
8
9
|
/**
|
|
9
|
-
* Resolve the workbench app for a CLI config, or `null` for a plain project
|
|
10
|
+
* Resolve the workbench app for a CLI config, or `null` for a plain project or a
|
|
11
|
+
* config (which is not an app — read it with `resolveWorkbenchConfig`).
|
|
10
12
|
* @public
|
|
11
13
|
*/ export function resolveWorkbenchApp(cliConfig) {
|
|
12
14
|
const app = cliConfig?.app;
|
|
15
|
+
// A config carries a distinct brand, so `isWorkbenchApp` is false for it — it
|
|
16
|
+
// resolves to `null` here, like a plain project.
|
|
13
17
|
if (!isWorkbenchApp(app)) return null;
|
|
14
18
|
const errors = validateWorkbenchApp(app);
|
|
15
19
|
if (errors.length > 0) throw new Error(formatWorkbenchAppErrors(errors));
|
|
16
20
|
return {
|
|
17
21
|
applicationType: app.applicationType,
|
|
18
|
-
config: readConfig(app),
|
|
19
22
|
entry: app.entry,
|
|
20
23
|
icon: app.icon,
|
|
21
|
-
|
|
24
|
+
// Identity defaults to the address, so existing apps behave identically.
|
|
25
|
+
name: app.name ?? app.slug,
|
|
22
26
|
organizationId: app.organizationId,
|
|
23
|
-
services: app.services ?? [],
|
|
24
27
|
slug: app.slug,
|
|
25
28
|
views: app.views ?? [],
|
|
26
|
-
visibility: app.visibility
|
|
29
|
+
visibility: app.visibility,
|
|
30
|
+
webWorkers: app.webWorkers ?? []
|
|
27
31
|
};
|
|
28
32
|
}
|
|
29
33
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/resolveWorkbenchApp.ts"],"sourcesContent":["// Package-internal shared resolver: turn a CLI config's branded\n// `
|
|
1
|
+
{"version":3,"sources":["../src/resolveWorkbenchApp.ts"],"sourcesContent":["// Package-internal shared resolver: turn a CLI config's branded\n// `defineApplication` app into its declared interfaces, or `null` for a plain\n// project — and for a config (a branded `unstable_defineMediaLibrary`), which\n// is not an app. The build and deploy accessors (actions/build, actions/deploy)\n// each build their command-specific view on top of this one brand-check +\n// extraction, so the discrimination lives in exactly one place.\n\nimport {type AppVisibility, type CliConfig} from '@sanity/cli-core'\n\nimport {type WorkbenchConfigValue} from './contract.js'\nimport {type DefineAppInput, isWorkbenchApp} from './defineApp.js'\nimport {formatWorkbenchAppErrors, validateWorkbenchApp} from './validateWorkbenchApp.js'\n\n/**\n * Bundled so adding a declaration family touches this type and the artifact\n * expanders, not every hop of build/dev plumbing in between. `config` is sourced\n * from `resolveWorkbenchConfig` (a config is not an app), not from this resolver.\n * @internal\n */\nexport interface WorkbenchExposes {\n config?: WorkbenchConfigValue\n views?: DefineAppInput['views']\n webWorkers?: DefineAppInput['webWorkers']\n}\n\n/** @public */\nexport interface ResolvedWorkbenchApp {\n /**\n * Stable identity, distinct from the `slug` address. Defaults to `slug`, and is\n * what every identity key (build id, interface ids) is built from.\n */\n readonly name: string\n /** Organization that owns the app — part of its build-id identity. */\n readonly organizationId: string\n readonly slug: string\n\n /** Views the app declares. */\n readonly views: NonNullable<DefineAppInput['views']>\n\n /** Background web workers the app declares. */\n readonly webWorkers: NonNullable<DefineAppInput['webWorkers']>\n\n /** Resolved app kind — `studio` or one of the SDK app types. */\n readonly applicationType?: string\n /** SDK app-view entrypoint, when declared. */\n readonly entry?: string\n /** Path to the app's icon SVG, resolved and shipped to Brett on deploy. */\n readonly icon?: string\n /** Dashboard visibility declared by the app; `undefined` when unset. */\n readonly visibility?: AppVisibility\n}\n\n/**\n * Resolve the workbench app for a CLI config, or `null` for a plain project or a\n * config (which is not an app — read it with `resolveWorkbenchConfig`).\n * @public\n */\nexport function resolveWorkbenchApp(\n cliConfig: CliConfig | null | undefined,\n): ResolvedWorkbenchApp | null {\n const app = cliConfig?.app\n // A config carries a distinct brand, so `isWorkbenchApp` is false for it — it\n // resolves to `null` here, like a plain project.\n if (!isWorkbenchApp(app)) return null\n\n const errors = validateWorkbenchApp(app)\n if (errors.length > 0) throw new Error(formatWorkbenchAppErrors(errors))\n\n return {\n applicationType: app.applicationType,\n entry: app.entry,\n icon: app.icon,\n // Identity defaults to the address, so existing apps behave identically.\n name: app.name ?? app.slug,\n organizationId: app.organizationId,\n slug: app.slug,\n views: app.views ?? [],\n visibility: app.visibility,\n webWorkers: app.webWorkers ?? [],\n }\n}\n"],"names":["isWorkbenchApp","formatWorkbenchAppErrors","validateWorkbenchApp","resolveWorkbenchApp","cliConfig","app","errors","length","Error","applicationType","entry","icon","name","slug","organizationId","views","visibility","webWorkers"],"mappings":"AAAA,gEAAgE;AAChE,8EAA8E;AAC9E,8EAA8E;AAC9E,gFAAgF;AAChF,0EAA0E;AAC1E,gEAAgE;AAKhE,SAA6BA,cAAc,QAAO,iBAAgB;AAClE,SAAQC,wBAAwB,EAAEC,oBAAoB,QAAO,4BAA2B;AAyCxF;;;;CAIC,GACD,OAAO,SAASC,oBACdC,SAAuC;IAEvC,MAAMC,MAAMD,WAAWC;IACvB,8EAA8E;IAC9E,iDAAiD;IACjD,IAAI,CAACL,eAAeK,MAAM,OAAO;IAEjC,MAAMC,SAASJ,qBAAqBG;IACpC,IAAIC,OAAOC,MAAM,GAAG,GAAG,MAAM,IAAIC,MAAMP,yBAAyBK;IAEhE,OAAO;QACLG,iBAAiBJ,IAAII,eAAe;QACpCC,OAAOL,IAAIK,KAAK;QAChBC,MAAMN,IAAIM,IAAI;QACd,yEAAyE;QACzEC,MAAMP,IAAIO,IAAI,IAAIP,IAAIQ,IAAI;QAC1BC,gBAAgBT,IAAIS,cAAc;QAClCD,MAAMR,IAAIQ,IAAI;QACdE,OAAOV,IAAIU,KAAK,IAAI,EAAE;QACtBC,YAAYX,IAAIW,UAAU;QAC1BC,YAAYZ,IAAIY,UAAU,IAAI,EAAE;IAClC;AACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Package-internal shared resolver: turn a CLI config's branded
|
|
2
|
+
// `unstable_defineMediaLibrary` config into its target `appType`, owning
|
|
3
|
+
// `organizationId`, and `fields`, or `null` when the config is not a config
|
|
4
|
+
// (a plain project or an app). The deploy/undeploy/build paths consult this
|
|
5
|
+
// second resolver; app-only callers keep using `resolveWorkbenchApp`.
|
|
6
|
+
import { ConfigSchema } from './contract.js';
|
|
7
|
+
import { isWorkbenchConfig } from './defineApp.js';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the workbench config for a CLI config, or `null` when it carries no
|
|
10
|
+
* config brand (a plain project or an app). The only config today is the Media
|
|
11
|
+
* Library, so the resolved shape is a {@link ResolvedMediaLibraryConfig}.
|
|
12
|
+
* @public
|
|
13
|
+
*/ export function resolveWorkbenchConfig(cliConfig) {
|
|
14
|
+
const app = cliConfig?.app;
|
|
15
|
+
if (!isWorkbenchConfig(app)) return null;
|
|
16
|
+
const result = ConfigSchema.safeParse({
|
|
17
|
+
appType: app.appType,
|
|
18
|
+
fields: app.fields
|
|
19
|
+
});
|
|
20
|
+
if (!result.success) {
|
|
21
|
+
throw new Error(`Invalid workbench config: ${result.error.message}`);
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
appType: app.appType,
|
|
25
|
+
fields: app.fields,
|
|
26
|
+
organizationId: app.organizationId
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=resolveWorkbenchConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/resolveWorkbenchConfig.ts"],"sourcesContent":["// Package-internal shared resolver: turn a CLI config's branded\n// `unstable_defineMediaLibrary` config into its target `appType`, owning\n// `organizationId`, and `fields`, or `null` when the config is not a config\n// (a plain project or an app). The deploy/undeploy/build paths consult this\n// second resolver; app-only callers keep using `resolveWorkbenchApp`.\n\nimport {type CliConfig} from '@sanity/cli-core'\n\nimport {ConfigSchema, type WorkbenchConfigValue} from './contract.js'\nimport {isWorkbenchConfig} from './defineApp.js'\n\n/** @public */\nexport interface ResolvedMediaLibraryConfig {\n /** Target app the config binds to — resolves the org installation on deploy. */\n readonly appType: WorkbenchConfigValue['appType']\n /** The config's declared fields, expanded into the federation remote on build. */\n readonly fields: WorkbenchConfigValue['fields']\n /** Organization that owns the config's target installation. */\n readonly organizationId: string\n}\n\n/**\n * Resolve the workbench config for a CLI config, or `null` when it carries no\n * config brand (a plain project or an app). The only config today is the Media\n * Library, so the resolved shape is a {@link ResolvedMediaLibraryConfig}.\n * @public\n */\nexport function resolveWorkbenchConfig(\n cliConfig: CliConfig | null | undefined,\n): ResolvedMediaLibraryConfig | null {\n const app = cliConfig?.app\n if (!isWorkbenchConfig(app)) return null\n\n const result = ConfigSchema.safeParse({appType: app.appType, fields: app.fields})\n if (!result.success) {\n throw new Error(`Invalid workbench config: ${result.error.message}`)\n }\n\n return {appType: app.appType, fields: app.fields, organizationId: app.organizationId}\n}\n"],"names":["ConfigSchema","isWorkbenchConfig","resolveWorkbenchConfig","cliConfig","app","result","safeParse","appType","fields","success","Error","error","message","organizationId"],"mappings":"AAAA,gEAAgE;AAChE,yEAAyE;AACzE,4EAA4E;AAC5E,4EAA4E;AAC5E,sEAAsE;AAItE,SAAQA,YAAY,QAAkC,gBAAe;AACrE,SAAQC,iBAAiB,QAAO,iBAAgB;AAYhD;;;;;CAKC,GACD,OAAO,SAASC,uBACdC,SAAuC;IAEvC,MAAMC,MAAMD,WAAWC;IACvB,IAAI,CAACH,kBAAkBG,MAAM,OAAO;IAEpC,MAAMC,SAASL,aAAaM,SAAS,CAAC;QAACC,SAASH,IAAIG,OAAO;QAAEC,QAAQJ,IAAII,MAAM;IAAA;IAC/E,IAAI,CAACH,OAAOI,OAAO,EAAE;QACnB,MAAM,IAAIC,MAAM,CAAC,0BAA0B,EAAEL,OAAOM,KAAK,CAACC,OAAO,EAAE;IACrE;IAEA,OAAO;QAACL,SAASH,IAAIG,OAAO;QAAEC,QAAQJ,IAAII,MAAM;QAAEK,gBAAgBT,IAAIS,cAAc;IAAA;AACtF"}
|