@sanity/workbench-cli 2.2.3 → 2.3.0
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/_exports/build.d.ts +1 -1
- package/dist/_exports/{defineApp-DldjQwq6.d.ts → defineApp-BRFgHrPc.d.ts} +8 -7
- package/dist/_exports/deploy.d.ts +4 -3
- package/dist/_exports/dev.d.ts +1 -1
- package/dist/_exports/index.d.ts +3 -3
- package/dist/_exports/preview.d.ts +1 -1
- package/dist/_exports/{registry-HE_dEHFa.d.ts → registry-CexFxsKR.d.ts} +2 -2
- package/dist/_exports/{resolveWorkbenchConfig-6iznHbED.d.ts → resolveWorkbenchConfig-CDUdRIjD.d.ts} +4 -2
- package/dist/_exports/{summarizeInterfaces-CjkeWwkY.d.ts → summarizeInterfaces-BQ1v1O89.d.ts} +4 -4
- package/dist/_exports/undeploy.d.ts +2 -2
- package/dist/actions/build/vite/plugin.js +4 -4
- package/dist/actions/build/vite/plugin.js.map +1 -1
- package/dist/actions/build/vite/plugins/plugin-sanity-environment.js.map +1 -1
- package/dist/actions/deploy/deployWorkbenchApp.js +5 -5
- package/dist/actions/deploy/deployWorkbenchApp.js.map +1 -1
- package/dist/actions/deploy/summarizeInterfaces.js.map +1 -1
- package/dist/actions/deploy/viewDeployment.js +2 -2
- package/dist/actions/deploy/viewDeployment.js.map +1 -1
- package/dist/actions/dev/appServerSupervisor.js.map +1 -1
- package/dist/actions/dev/registry.js +1 -1
- package/dist/actions/dev/registry.js.map +1 -1
- package/dist/contract.js +9 -9
- package/dist/contract.js.map +1 -1
- package/dist/defineApp.js +1 -0
- package/dist/defineApp.js.map +1 -1
- package/dist/defineView.js.map +1 -1
- package/dist/deriveInterfaces.js +5 -5
- package/dist/deriveInterfaces.js.map +1 -1
- package/dist/resolveWorkbenchApp.js +3 -0
- package/dist/resolveWorkbenchApp.js.map +1 -1
- package/package.json +9 -9
package/dist/_exports/build.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as resolveWorkbenchApp, i as WorkbenchExposes, n as resolveWorkbenchConfig, r as ResolvedWorkbenchApp, t as ResolvedMediaLibraryConfig } from "./resolveWorkbenchConfig-
|
|
1
|
+
import { a as resolveWorkbenchApp, i as WorkbenchExposes, n as resolveWorkbenchConfig, r as ResolvedWorkbenchApp, t as ResolvedMediaLibraryConfig } from "./resolveWorkbenchConfig-CDUdRIjD.js";
|
|
2
2
|
import { PluginOption } from "vite";
|
|
3
3
|
/**
|
|
4
4
|
* Dep pre-bundling inputs for a workbench app's dev server.
|
|
@@ -15,10 +15,10 @@ interface ViewComponentBaseProps<TView> {
|
|
|
15
15
|
}
|
|
16
16
|
/** Component slots per view surface; windows load outside the component artifact path. @internal */
|
|
17
17
|
declare const VIEW_COMPONENTS: {
|
|
18
|
-
readonly app: readonly [];
|
|
19
18
|
readonly asset_source: readonly ["asset_source"];
|
|
20
19
|
readonly panel: readonly ["title", "panel"];
|
|
21
20
|
readonly tile: readonly ["tile"];
|
|
21
|
+
readonly window: readonly [];
|
|
22
22
|
};
|
|
23
23
|
/** @public */
|
|
24
24
|
type ViewSurface = keyof typeof VIEW_COMPONENTS;
|
|
@@ -77,7 +77,7 @@ declare const WindowViewSchema: z.ZodMiniObject<{
|
|
|
77
77
|
title: z.ZodMiniString<string>;
|
|
78
78
|
name: z.ZodMiniString<string>;
|
|
79
79
|
src: z.ZodMiniString<string>;
|
|
80
|
-
surface: z.ZodMiniLiteral<"
|
|
80
|
+
surface: z.ZodMiniLiteral<"window">;
|
|
81
81
|
}, z.core.$strip>;
|
|
82
82
|
/** @public */
|
|
83
83
|
type WindowView = z.output<typeof WindowViewSchema>;
|
|
@@ -128,7 +128,7 @@ declare const InterfaceDeclarationSchema: z.ZodMiniDiscriminatedUnion<[z.ZodMini
|
|
|
128
128
|
title: z.ZodMiniString<string>;
|
|
129
129
|
name: z.ZodMiniString<string>;
|
|
130
130
|
src: z.ZodMiniString<string>;
|
|
131
|
-
surface: z.ZodMiniLiteral<"
|
|
131
|
+
surface: z.ZodMiniLiteral<"window">;
|
|
132
132
|
}, z.core.$strip>, z.ZodMiniObject<{
|
|
133
133
|
dock: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
134
134
|
group: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
@@ -214,6 +214,7 @@ declare const DefineAppInputSchema: z.ZodMiniObject<{
|
|
|
214
214
|
}, z.core.$strip>>;
|
|
215
215
|
entry: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
216
216
|
icon: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
217
|
+
isSingleton: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
217
218
|
name: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
218
219
|
organizationId: z.ZodMiniString<string>;
|
|
219
220
|
slug: z.ZodMiniString<string>;
|
|
@@ -230,7 +231,7 @@ declare const DefineAppInputSchema: z.ZodMiniObject<{
|
|
|
230
231
|
title: z.ZodMiniString<string>;
|
|
231
232
|
name: z.ZodMiniString<string>;
|
|
232
233
|
src: z.ZodMiniString<string>;
|
|
233
|
-
surface: z.ZodMiniLiteral<"
|
|
234
|
+
surface: z.ZodMiniLiteral<"window">;
|
|
234
235
|
}, z.core.$strip>, z.ZodMiniObject<{
|
|
235
236
|
dock: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
236
237
|
group: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
@@ -275,10 +276,10 @@ declare const DefineAppInputSchema: z.ZodMiniObject<{
|
|
|
275
276
|
}, z.core.$strip>;
|
|
276
277
|
/**
|
|
277
278
|
* User-facing input for `defineApplication`. Excludes the internal
|
|
278
|
-
* `applicationType`.
|
|
279
|
+
* `applicationType` and `isSingleton`.
|
|
279
280
|
* @public
|
|
280
281
|
*/
|
|
281
|
-
type DefineAppInput = Omit<z.output<typeof DefineAppInputSchema>, 'applicationType'>;
|
|
282
|
+
type DefineAppInput = Omit<z.output<typeof DefineAppInputSchema>, 'applicationType' | 'isSingleton'>;
|
|
282
283
|
/**
|
|
283
284
|
* Nominal brand the CLI discriminates on to enable the workbench build/deploy
|
|
284
285
|
* codepath. Registered via `Symbol.for` so the marker survives module-realm
|
|
@@ -372,4 +373,4 @@ declare function isWorkbenchConfig(app: unknown): app is WorkbenchConfig;
|
|
|
372
373
|
*/
|
|
373
374
|
declare function unstable_defineMediaLibrary(input: DefineMediaLibraryInput): WorkbenchConfig;
|
|
374
375
|
export { WorkbenchConfigValue as A, VIEW_CONTRACT_VERSION as C, ViewSurface as D, ViewDeclaration as E, defineWindowView as F, definePanelView as M, defineTileView as N, WebWorker as O, defineWebWorker as P, TileView as S, ViewComponentBaseProps as T, DockGroup as _, WorkbenchApp as a, ServiceType as b, isWorkbenchApp as c, AssetSourceView as d, DefineAssetSourceViewInput as f, DefineWindowViewInput as g, DefineWebWorkerInput as h, MediaLibraryField as i, defineAssetSourceView as j, WindowView as k, isWorkbenchConfig as l, DefineTileViewInput as m, DefineAppResult as n, WorkbenchConfig as o, DefinePanelViewInput as p, DefineMediaLibraryInput as r, defineApplication as s, DefineAppInput as t, unstable_defineMediaLibrary as u, PanelView as v, ViewComponent as w, TileSize as x, SERVICE_CONTRACT_VERSION as y };
|
|
375
|
-
//# sourceMappingURL=defineApp-
|
|
376
|
+
//# sourceMappingURL=defineApp-BRFgHrPc.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./defineApp-
|
|
2
|
-
import { n as resolveWorkbenchConfig, t as ResolvedMediaLibraryConfig } from "./resolveWorkbenchConfig-
|
|
3
|
-
import { a as DeployableWorkbenchApp, i as summarizeInterfaces, n as DeployedView, o as getWorkbench, r as DeployedWebWorker, t as DeployedInterface } from "./summarizeInterfaces-
|
|
1
|
+
import "./defineApp-BRFgHrPc.js";
|
|
2
|
+
import { n as resolveWorkbenchConfig, t as ResolvedMediaLibraryConfig } from "./resolveWorkbenchConfig-CDUdRIjD.js";
|
|
3
|
+
import { a as DeployableWorkbenchApp, i as summarizeInterfaces, n as DeployedView, o as getWorkbench, r as DeployedWebWorker, t as DeployedInterface } from "./summarizeInterfaces-BQ1v1O89.js";
|
|
4
4
|
import { AppVisibility, CliConfig, Output } from "@sanity/cli-core";
|
|
5
5
|
import "node:zlib";
|
|
6
6
|
/**
|
|
@@ -117,6 +117,7 @@ interface CreatedApplication {
|
|
|
117
117
|
* @internal
|
|
118
118
|
*/
|
|
119
119
|
declare function createCoreApp(options: {
|
|
120
|
+
isSingleton?: boolean;
|
|
120
121
|
name?: string;
|
|
121
122
|
organizationId: string;
|
|
122
123
|
slug: string;
|
package/dist/_exports/dev.d.ts
CHANGED
package/dist/_exports/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as VIEW_CONTRACT_VERSION, D as ViewSurface, E as ViewDeclaration, F as defineWindowView, M as definePanelView, N as defineTileView, O as WebWorker, P as defineWebWorker, S as TileView, T as ViewComponentBaseProps, _ as DockGroup, a as WorkbenchApp, b as ServiceType, c as isWorkbenchApp, d as AssetSourceView, f as DefineAssetSourceViewInput, g as DefineWindowViewInput, h as DefineWebWorkerInput, i as MediaLibraryField, j as defineAssetSourceView, k as WindowView, l as isWorkbenchConfig, m as DefineTileViewInput, n as DefineAppResult, o as WorkbenchConfig, p as DefinePanelViewInput, r as DefineMediaLibraryInput, s as defineApplication, t as DefineAppInput, u as unstable_defineMediaLibrary, v as PanelView, w as ViewComponent, x as TileSize, y as SERVICE_CONTRACT_VERSION } from "./defineApp-
|
|
1
|
+
import { C as VIEW_CONTRACT_VERSION, D as ViewSurface, E as ViewDeclaration, F as defineWindowView, M as definePanelView, N as defineTileView, O as WebWorker, P as defineWebWorker, S as TileView, T as ViewComponentBaseProps, _ as DockGroup, a as WorkbenchApp, b as ServiceType, c as isWorkbenchApp, d as AssetSourceView, f as DefineAssetSourceViewInput, g as DefineWindowViewInput, h as DefineWebWorkerInput, i as MediaLibraryField, j as defineAssetSourceView, k as WindowView, l as isWorkbenchConfig, m as DefineTileViewInput, n as DefineAppResult, o as WorkbenchConfig, p as DefinePanelViewInput, r as DefineMediaLibraryInput, s as defineApplication, t as DefineAppInput, u as unstable_defineMediaLibrary, v as PanelView, w as ViewComponent, x as TileSize, y as SERVICE_CONTRACT_VERSION } from "./defineApp-BRFgHrPc.js";
|
|
2
2
|
import { AssetSourceComponentProps } from "@sanity/types";
|
|
3
3
|
/**
|
|
4
4
|
* The service's own declaration, surfaced to the callback.
|
|
@@ -52,7 +52,7 @@ declare function unstable_defineService<TType extends ServiceType>(type: TType,
|
|
|
52
52
|
type WindowViewProps = ViewComponentBaseProps<{
|
|
53
53
|
name: string;
|
|
54
54
|
src: string;
|
|
55
|
-
surface: '
|
|
55
|
+
surface: 'window';
|
|
56
56
|
title: string;
|
|
57
57
|
}>;
|
|
58
58
|
/**
|
|
@@ -130,10 +130,10 @@ type TileComponent = keyof TileViewComponents;
|
|
|
130
130
|
* @public
|
|
131
131
|
*/
|
|
132
132
|
interface ViewComponentsBySurface {
|
|
133
|
-
app: ViewComponent<WindowViewProps>;
|
|
134
133
|
asset_source: AssetSourceViewComponents;
|
|
135
134
|
panel: PanelViewComponents;
|
|
136
135
|
tile: TileViewComponents;
|
|
136
|
+
window: ViewComponent<WindowViewProps>;
|
|
137
137
|
}
|
|
138
138
|
/**
|
|
139
139
|
* The result of `unstable_defineView`: the author's component(s), the view surface,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as DevServerManifest } from "./registry-
|
|
1
|
+
import { t as DevServerManifest } from "./registry-CexFxsKR.js";
|
|
2
2
|
import { CliConfig, Output } from "@sanity/cli-core";
|
|
3
3
|
interface StartWorkbenchPreviewOptions {
|
|
4
4
|
/** Directory for the workbench Vite server's dependency cache. */
|
|
@@ -25,7 +25,7 @@ declare const devServerManifestSchema: z.ZodMiniObject<{
|
|
|
25
25
|
order: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
26
26
|
}, z.core.$strip>;
|
|
27
27
|
}, z.core.$strip>>;
|
|
28
|
-
surface: z.ZodMiniLiteral<"
|
|
28
|
+
surface: z.ZodMiniLiteral<"window">;
|
|
29
29
|
id: z.ZodMiniString<string>;
|
|
30
30
|
moduleId: z.ZodMiniString<string>;
|
|
31
31
|
name: z.ZodMiniString<string>;
|
|
@@ -122,4 +122,4 @@ declare const devServerManifestSchema: z.ZodMiniObject<{
|
|
|
122
122
|
*/
|
|
123
123
|
type DevServerManifest = z.infer<typeof devServerManifestSchema>;
|
|
124
124
|
export { DevServerManifest as t };
|
|
125
|
-
//# sourceMappingURL=registry-
|
|
125
|
+
//# sourceMappingURL=registry-CexFxsKR.d.ts.map
|
package/dist/_exports/{resolveWorkbenchConfig-6iznHbED.d.ts → resolveWorkbenchConfig-CDUdRIjD.d.ts}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as WorkbenchConfigValue, t as DefineAppInput } from "./defineApp-
|
|
1
|
+
import { A as WorkbenchConfigValue, t as DefineAppInput } from "./defineApp-BRFgHrPc.js";
|
|
2
2
|
import { AppVisibility, CliConfig } from "@sanity/cli-core";
|
|
3
3
|
/**
|
|
4
4
|
* Bundled so adding a declaration family touches this type and the artifact
|
|
@@ -31,6 +31,8 @@ interface ResolvedWorkbenchApp {
|
|
|
31
31
|
readonly entry?: string;
|
|
32
32
|
/** Path to the app's icon SVG, resolved and shipped to Brett on deploy. */
|
|
33
33
|
readonly icon?: string;
|
|
34
|
+
/** Explicit singleton flag for Sanity-owned apps. */
|
|
35
|
+
readonly isSingleton?: boolean;
|
|
34
36
|
/** Dashboard visibility declared by the app; `undefined` when unset. */
|
|
35
37
|
readonly visibility?: AppVisibility;
|
|
36
38
|
}
|
|
@@ -57,4 +59,4 @@ interface ResolvedMediaLibraryConfig {
|
|
|
57
59
|
*/
|
|
58
60
|
declare function resolveWorkbenchConfig(cliConfig: CliConfig | null | undefined): ResolvedMediaLibraryConfig | null;
|
|
59
61
|
export { resolveWorkbenchApp as a, WorkbenchExposes as i, resolveWorkbenchConfig as n, ResolvedWorkbenchApp as r, ResolvedMediaLibraryConfig as t };
|
|
60
|
-
//# sourceMappingURL=resolveWorkbenchConfig-
|
|
62
|
+
//# sourceMappingURL=resolveWorkbenchConfig-CDUdRIjD.d.ts.map
|
package/dist/_exports/{summarizeInterfaces-CjkeWwkY.d.ts → summarizeInterfaces-BQ1v1O89.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as ViewSurface, b as ServiceType } from "./defineApp-
|
|
2
|
-
import { i as WorkbenchExposes, r as ResolvedWorkbenchApp } from "./resolveWorkbenchConfig-
|
|
1
|
+
import { D as ViewSurface, b as ServiceType } from "./defineApp-BRFgHrPc.js";
|
|
2
|
+
import { i as WorkbenchExposes, r as ResolvedWorkbenchApp } from "./resolveWorkbenchConfig-CDUdRIjD.js";
|
|
3
3
|
import { CliConfig } from "@sanity/cli-core";
|
|
4
4
|
import { z } from "zod/mini";
|
|
5
5
|
/**
|
|
@@ -15,7 +15,7 @@ declare const viewDeploymentPayloadSchema: z.ZodMiniObject<{
|
|
|
15
15
|
name: z.ZodMiniString<string>;
|
|
16
16
|
src: z.ZodMiniString<string>;
|
|
17
17
|
type: z.ZodMiniEnum<{
|
|
18
|
-
|
|
18
|
+
window: "window";
|
|
19
19
|
panel: "panel";
|
|
20
20
|
asset_source: "asset_source";
|
|
21
21
|
tile: "tile";
|
|
@@ -62,4 +62,4 @@ declare function summarizeInterfaces({ views, webWorkers }: WorkbenchExposes): {
|
|
|
62
62
|
views: DeployedView[];
|
|
63
63
|
};
|
|
64
64
|
export { DeployableWorkbenchApp as a, summarizeInterfaces as i, DeployedView as n, getWorkbench as o, DeployedWebWorker as r, DeployedInterface as t };
|
|
65
|
-
//# sourceMappingURL=summarizeInterfaces-
|
|
65
|
+
//# sourceMappingURL=summarizeInterfaces-BQ1v1O89.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as ResolvedMediaLibraryConfig } from "./resolveWorkbenchConfig-
|
|
2
|
-
import { a as DeployableWorkbenchApp, n as DeployedView, r as DeployedWebWorker } from "./summarizeInterfaces-
|
|
1
|
+
import { t as ResolvedMediaLibraryConfig } from "./resolveWorkbenchConfig-CDUdRIjD.js";
|
|
2
|
+
import { a as DeployableWorkbenchApp, n as DeployedView, r as DeployedWebWorker } from "./summarizeInterfaces-BQ1v1O89.js";
|
|
3
3
|
import "node:zlib";
|
|
4
4
|
import { UndeployAdapter, UndeployApplicationTarget, UndeployConfigTarget } from "@sanity/cli-core/undeploy";
|
|
5
5
|
interface ConfigSnapshot {
|
|
@@ -48,10 +48,10 @@ import { sanityFederationRuntime } from './plugins/plugin-sanity-federation-runt
|
|
|
48
48
|
isApp: false,
|
|
49
49
|
studioConfigPath: options.studioConfigPath
|
|
50
50
|
};
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
const clientInput =
|
|
51
|
+
// Every federated app and studio also serves itself standalone. Whenever there's
|
|
52
|
+
// an `./App` to mount (never for a dock-only app), build the SPA client
|
|
53
|
+
// environment from the runtime bootstrap cli-build writes.
|
|
54
|
+
const clientInput = exposesApp ? path.join(workDir, '.sanity', 'runtime', 'app.js') : undefined;
|
|
55
55
|
return [
|
|
56
56
|
sanityEnvironmentPlugin({
|
|
57
57
|
clientInput,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/build/vite/plugin.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type ModuleFederationOptions} from '@module-federation/vite'\nimport {type PackageJson} from '@sanity/cli-core'\nimport {type PluginOption} from 'vite'\n\nimport {type WorkbenchExposes} from '../../../resolveWorkbenchApp.js'\nimport {artifactExposes, workbenchArtifacts} from '../artifact.js'\nimport {FEDERATION_FILE_NAME, RUNTIME_DIR} from './constants.js'\nimport {type FederationOptions, sanityModuleFederation} from './plugins/plugin-module-federation.js'\nimport {sanityEnvironmentPlugin} from './plugins/plugin-sanity-environment.js'\nimport {sanityExtensionArtifacts} from './plugins/plugin-sanity-extension-artifacts.js'\nimport {\n type FederationRuntimeOptions,\n sanityFederationRuntime,\n} from './plugins/plugin-sanity-federation-runtime.js'\n\ninterface FederationPluginOptionsBase extends Omit<Partial<FederationOptions>, 'exposes'> {\n exposes?: WorkbenchExposes\n pkgJson?: PackageJson\n /**\n * Current working directory to read package.json from, defaults to process.cwd()\n */\n workDir?: string\n}\n\ninterface AppFederationPluginOptions extends FederationPluginOptionsBase {\n isApp: true\n\n /**\n * Relative path to the App entry from the runtime directory, e.g.\n * `../../src/App.tsx`. Omit it for a dock-only app that declares no `entry`:\n * no `./App` is exposed and the remote serves only its views.\n */\n appEntry?: string\n studioConfigPath?: never\n}\n\ninterface StudioFederationPluginOptions extends FederationPluginOptionsBase {\n /** relative path to the Studio config file from the runtime directory (e.g. `../../sanity.config.ts`). */\n studioConfigPath: string\n\n appEntry?: never\n /** @defaultValue false */\n isApp?: false\n}\n\n/**\n * Plugin options for the federation vite plugin.\n *\n * Discriminated on `isApp`:\n * - `isApp: true` → requires `appEntry`\n * - `isApp: false` (default) → requires `studioConfigPath`\n *\n * @internal\n */\ntype FederationPluginOptions = AppFederationPluginOptions | StudioFederationPluginOptions\n\n/**\n * @internal\n */\nexport const federation = (options: FederationPluginOptions): PluginOption => {\n const {exposes, name: defaultName, pkgJson, workDir = process.cwd()} = options\n\n let name = defaultName\n\n if (!name) {\n name = pkgJson?.name\n }\n\n if (!name) {\n throw new Error('\"name\" option is required but could not be inferred from package.json')\n }\n\n const generatedEntry = `./${RUNTIME_DIR}/${FEDERATION_FILE_NAME}.jsx`\n\n function resolveEntryPath(entry: string) {\n const resolvedPath = path.resolve(workDir, entry)\n\n if (!resolvedPath) {\n throw new Error(\n `Could not resolve path for entry \"${entry}\". Please check that the file exists and the path is correct.`,\n )\n }\n\n return resolvedPath\n }\n\n const entryPath = resolveEntryPath(generatedEntry)\n\n // Each view component (`./views/<view>/<component>`) and each service loader\n // (`./services/<name>`) is exposed straight to the host, pointing at the file\n // the extension-artifacts plugin generates under RUNTIME_DIR. A service's\n // worker bundle carries no expose — the host reaches it through its loader.\n const artifacts = workbenchArtifacts(exposes ?? {})\n const artifactModuleExposes = artifactExposes(artifacts, (artifactPath) =>\n resolveEntryPath(`./${RUNTIME_DIR}/${artifactPath}`),\n )\n\n // A dock-only app (`isApp` with no `appEntry`) has no navigable full-page\n // view, so it exposes no `./App` — only its views. Studios and apps with an\n // entry expose `./App` (the generated render entry).\n const exposesApp = !options.isApp || options.appEntry !== undefined\n\n const federationExposes: NonNullable<ModuleFederationOptions['exposes']> = {\n ...(exposesApp ? {'./App': entryPath} : {}),\n ...artifactModuleExposes,\n }\n\n const runtimeOptions: FederationRuntimeOptions = options.isApp\n ? {appEntry: options.appEntry, isApp: true}\n : {isApp: false, studioConfigPath: options.studioConfigPath}\n\n //
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/build/vite/plugin.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type ModuleFederationOptions} from '@module-federation/vite'\nimport {type PackageJson} from '@sanity/cli-core'\nimport {type PluginOption} from 'vite'\n\nimport {type WorkbenchExposes} from '../../../resolveWorkbenchApp.js'\nimport {artifactExposes, workbenchArtifacts} from '../artifact.js'\nimport {FEDERATION_FILE_NAME, RUNTIME_DIR} from './constants.js'\nimport {type FederationOptions, sanityModuleFederation} from './plugins/plugin-module-federation.js'\nimport {sanityEnvironmentPlugin} from './plugins/plugin-sanity-environment.js'\nimport {sanityExtensionArtifacts} from './plugins/plugin-sanity-extension-artifacts.js'\nimport {\n type FederationRuntimeOptions,\n sanityFederationRuntime,\n} from './plugins/plugin-sanity-federation-runtime.js'\n\ninterface FederationPluginOptionsBase extends Omit<Partial<FederationOptions>, 'exposes'> {\n exposes?: WorkbenchExposes\n pkgJson?: PackageJson\n /**\n * Current working directory to read package.json from, defaults to process.cwd()\n */\n workDir?: string\n}\n\ninterface AppFederationPluginOptions extends FederationPluginOptionsBase {\n isApp: true\n\n /**\n * Relative path to the App entry from the runtime directory, e.g.\n * `../../src/App.tsx`. Omit it for a dock-only app that declares no `entry`:\n * no `./App` is exposed and the remote serves only its views.\n */\n appEntry?: string\n studioConfigPath?: never\n}\n\ninterface StudioFederationPluginOptions extends FederationPluginOptionsBase {\n /** relative path to the Studio config file from the runtime directory (e.g. `../../sanity.config.ts`). */\n studioConfigPath: string\n\n appEntry?: never\n /** @defaultValue false */\n isApp?: false\n}\n\n/**\n * Plugin options for the federation vite plugin.\n *\n * Discriminated on `isApp`:\n * - `isApp: true` → requires `appEntry`\n * - `isApp: false` (default) → requires `studioConfigPath`\n *\n * @internal\n */\ntype FederationPluginOptions = AppFederationPluginOptions | StudioFederationPluginOptions\n\n/**\n * @internal\n */\nexport const federation = (options: FederationPluginOptions): PluginOption => {\n const {exposes, name: defaultName, pkgJson, workDir = process.cwd()} = options\n\n let name = defaultName\n\n if (!name) {\n name = pkgJson?.name\n }\n\n if (!name) {\n throw new Error('\"name\" option is required but could not be inferred from package.json')\n }\n\n const generatedEntry = `./${RUNTIME_DIR}/${FEDERATION_FILE_NAME}.jsx`\n\n function resolveEntryPath(entry: string) {\n const resolvedPath = path.resolve(workDir, entry)\n\n if (!resolvedPath) {\n throw new Error(\n `Could not resolve path for entry \"${entry}\". Please check that the file exists and the path is correct.`,\n )\n }\n\n return resolvedPath\n }\n\n const entryPath = resolveEntryPath(generatedEntry)\n\n // Each view component (`./views/<view>/<component>`) and each service loader\n // (`./services/<name>`) is exposed straight to the host, pointing at the file\n // the extension-artifacts plugin generates under RUNTIME_DIR. A service's\n // worker bundle carries no expose — the host reaches it through its loader.\n const artifacts = workbenchArtifacts(exposes ?? {})\n const artifactModuleExposes = artifactExposes(artifacts, (artifactPath) =>\n resolveEntryPath(`./${RUNTIME_DIR}/${artifactPath}`),\n )\n\n // A dock-only app (`isApp` with no `appEntry`) has no navigable full-page\n // view, so it exposes no `./App` — only its views. Studios and apps with an\n // entry expose `./App` (the generated render entry).\n const exposesApp = !options.isApp || options.appEntry !== undefined\n\n const federationExposes: NonNullable<ModuleFederationOptions['exposes']> = {\n ...(exposesApp ? {'./App': entryPath} : {}),\n ...artifactModuleExposes,\n }\n\n const runtimeOptions: FederationRuntimeOptions = options.isApp\n ? {appEntry: options.appEntry, isApp: true}\n : {isApp: false, studioConfigPath: options.studioConfigPath}\n\n // Every federated app and studio also serves itself standalone. Whenever there's\n // an `./App` to mount (never for a dock-only app), build the SPA client\n // environment from the runtime bootstrap cli-build writes.\n const clientInput = exposesApp ? path.join(workDir, '.sanity', 'runtime', 'app.js') : undefined\n\n return [\n sanityEnvironmentPlugin({clientInput, input: entryPath}),\n sanityFederationRuntime(runtimeOptions),\n sanityExtensionArtifacts({artifacts}),\n sanityModuleFederation({exposes: federationExposes, name}),\n ]\n}\n"],"names":["path","artifactExposes","workbenchArtifacts","FEDERATION_FILE_NAME","RUNTIME_DIR","sanityModuleFederation","sanityEnvironmentPlugin","sanityExtensionArtifacts","sanityFederationRuntime","federation","options","exposes","name","defaultName","pkgJson","workDir","process","cwd","Error","generatedEntry","resolveEntryPath","entry","resolvedPath","resolve","entryPath","artifacts","artifactModuleExposes","artifactPath","exposesApp","isApp","appEntry","undefined","federationExposes","runtimeOptions","studioConfigPath","clientInput","join","input"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAO5B,SAAQC,eAAe,EAAEC,kBAAkB,QAAO,iBAAgB;AAClE,SAAQC,oBAAoB,EAAEC,WAAW,QAAO,iBAAgB;AAChE,SAAgCC,sBAAsB,QAAO,wCAAuC;AACpG,SAAQC,uBAAuB,QAAO,yCAAwC;AAC9E,SAAQC,wBAAwB,QAAO,iDAAgD;AACvF,SAEEC,uBAAuB,QAClB,gDAA+C;AA2CtD;;CAEC,GACD,OAAO,MAAMC,aAAa,CAACC;IACzB,MAAM,EAACC,OAAO,EAAEC,MAAMC,WAAW,EAAEC,OAAO,EAAEC,UAAUC,QAAQC,GAAG,EAAE,EAAC,GAAGP;IAEvE,IAAIE,OAAOC;IAEX,IAAI,CAACD,MAAM;QACTA,OAAOE,SAASF;IAClB;IAEA,IAAI,CAACA,MAAM;QACT,MAAM,IAAIM,MAAM;IAClB;IAEA,MAAMC,iBAAiB,CAAC,EAAE,EAAEf,YAAY,CAAC,EAAED,qBAAqB,IAAI,CAAC;IAErE,SAASiB,iBAAiBC,KAAa;QACrC,MAAMC,eAAetB,KAAKuB,OAAO,CAACR,SAASM;QAE3C,IAAI,CAACC,cAAc;YACjB,MAAM,IAAIJ,MACR,CAAC,kCAAkC,EAAEG,MAAM,6DAA6D,CAAC;QAE7G;QAEA,OAAOC;IACT;IAEA,MAAME,YAAYJ,iBAAiBD;IAEnC,6EAA6E;IAC7E,8EAA8E;IAC9E,0EAA0E;IAC1E,4EAA4E;IAC5E,MAAMM,YAAYvB,mBAAmBS,WAAW,CAAC;IACjD,MAAMe,wBAAwBzB,gBAAgBwB,WAAW,CAACE,eACxDP,iBAAiB,CAAC,EAAE,EAAEhB,YAAY,CAAC,EAAEuB,cAAc;IAGrD,0EAA0E;IAC1E,4EAA4E;IAC5E,qDAAqD;IACrD,MAAMC,aAAa,CAAClB,QAAQmB,KAAK,IAAInB,QAAQoB,QAAQ,KAAKC;IAE1D,MAAMC,oBAAqE;QACzE,GAAIJ,aAAa;YAAC,SAASJ;QAAS,IAAI,CAAC,CAAC;QAC1C,GAAGE,qBAAqB;IAC1B;IAEA,MAAMO,iBAA2CvB,QAAQmB,KAAK,GAC1D;QAACC,UAAUpB,QAAQoB,QAAQ;QAAED,OAAO;IAAI,IACxC;QAACA,OAAO;QAAOK,kBAAkBxB,QAAQwB,gBAAgB;IAAA;IAE7D,iFAAiF;IACjF,wEAAwE;IACxE,2DAA2D;IAC3D,MAAMC,cAAcP,aAAa5B,KAAKoC,IAAI,CAACrB,SAAS,WAAW,WAAW,YAAYgB;IAEtF,OAAO;QACLzB,wBAAwB;YAAC6B;YAAaE,OAAOb;QAAS;QACtDhB,wBAAwByB;QACxB1B,yBAAyB;YAACkB;QAAS;QACnCpB,uBAAuB;YAACM,SAASqB;YAAmBpB;QAAI;KACzD;AACH,EAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/actions/build/vite/plugins/plugin-sanity-environment.ts"],"sourcesContent":["import {type Plugin} from 'vite'\n\nimport {FEDERATION_DIR_NAME} from '../constants.js'\n\ninterface EnvironmentOptions {\n input: string\n\n /**\n * When set, also build a standalone `client` SPA environment (its own\n * `index.html` + bootstrap) alongside the federation remote, from this entry.\n *
|
|
1
|
+
{"version":3,"sources":["../../../../../src/actions/build/vite/plugins/plugin-sanity-environment.ts"],"sourcesContent":["import {type Plugin} from 'vite'\n\nimport {FEDERATION_DIR_NAME} from '../constants.js'\n\ninterface EnvironmentOptions {\n input: string\n\n /**\n * When set, also build a standalone `client` SPA environment (its own\n * `index.html` + bootstrap) alongside the federation remote, from this entry.\n * Set for every federated app and studio; omitted only for a dock-only app.\n */\n clientInput?: string\n}\n\nexport function sanityEnvironmentPlugin(options: EnvironmentOptions): Plugin {\n const {clientInput, input} = options\n\n return {\n config() {\n return {\n builder: {\n async buildApp(builder) {\n // `emptyOutDir` is false on both environments and the CLI clears\n // `dist` once up-front, so the SPA and federation outputs coexist\n // without either build wiping the other's files.\n if (clientInput) {\n await builder.build(builder.environments.client)\n }\n await builder.build(builder.environments[FEDERATION_DIR_NAME])\n },\n },\n environments: {\n ...(clientInput\n ? {\n client: {\n build: {\n assetsDir: 'static',\n copyPublicDir: false,\n emptyOutDir: false,\n outDir: `dist`,\n rolldownOptions: {input: {sanity: clientInput}},\n },\n consumer: 'client',\n },\n }\n : {}),\n [FEDERATION_DIR_NAME]: {\n build: {\n assetsDir: 'static',\n copyPublicDir: false,\n emptyOutDir: false,\n outDir: `dist`,\n rolldownOptions: {input},\n },\n consumer: 'client',\n },\n },\n }\n },\n name: 'sanity/environment',\n }\n}\n"],"names":["FEDERATION_DIR_NAME","sanityEnvironmentPlugin","options","clientInput","input","config","builder","buildApp","build","environments","client","assetsDir","copyPublicDir","emptyOutDir","outDir","rolldownOptions","sanity","consumer","name"],"mappings":"AAEA,SAAQA,mBAAmB,QAAO,kBAAiB;AAanD,OAAO,SAASC,wBAAwBC,OAA2B;IACjE,MAAM,EAACC,WAAW,EAAEC,KAAK,EAAC,GAAGF;IAE7B,OAAO;QACLG;YACE,OAAO;gBACLC,SAAS;oBACP,MAAMC,UAASD,OAAO;wBACpB,iEAAiE;wBACjE,kEAAkE;wBAClE,iDAAiD;wBACjD,IAAIH,aAAa;4BACf,MAAMG,QAAQE,KAAK,CAACF,QAAQG,YAAY,CAACC,MAAM;wBACjD;wBACA,MAAMJ,QAAQE,KAAK,CAACF,QAAQG,YAAY,CAACT,oBAAoB;oBAC/D;gBACF;gBACAS,cAAc;oBACZ,GAAIN,cACA;wBACEO,QAAQ;4BACNF,OAAO;gCACLG,WAAW;gCACXC,eAAe;gCACfC,aAAa;gCACbC,QAAQ,CAAC,IAAI,CAAC;gCACdC,iBAAiB;oCAACX,OAAO;wCAACY,QAAQb;oCAAW;gCAAC;4BAChD;4BACAc,UAAU;wBACZ;oBACF,IACA,CAAC,CAAC;oBACN,CAACjB,oBAAoB,EAAE;wBACrBQ,OAAO;4BACLG,WAAW;4BACXC,eAAe;4BACfC,aAAa;4BACbC,QAAQ,CAAC,IAAI,CAAC;4BACdC,iBAAiB;gCAACX;4BAAK;wBACzB;wBACAa,UAAU;oBACZ;gBACF;YACF;QACF;QACAC,MAAM;IACR;AACF"}
|
|
@@ -11,7 +11,7 @@ function toBrettInterface(iface, version) {
|
|
|
11
11
|
version
|
|
12
12
|
};
|
|
13
13
|
switch(declaration.surface){
|
|
14
|
-
case '
|
|
14
|
+
case 'asset_source':
|
|
15
15
|
{
|
|
16
16
|
const { surface, ...view } = declaration;
|
|
17
17
|
return {
|
|
@@ -20,7 +20,7 @@ function toBrettInterface(iface, version) {
|
|
|
20
20
|
version
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
case '
|
|
23
|
+
case 'panel':
|
|
24
24
|
{
|
|
25
25
|
const { surface, ...view } = declaration;
|
|
26
26
|
return {
|
|
@@ -29,7 +29,7 @@ function toBrettInterface(iface, version) {
|
|
|
29
29
|
version
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
-
case '
|
|
32
|
+
case 'tile':
|
|
33
33
|
{
|
|
34
34
|
const { surface, ...view } = declaration;
|
|
35
35
|
return {
|
|
@@ -38,12 +38,12 @@ function toBrettInterface(iface, version) {
|
|
|
38
38
|
version
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
case '
|
|
41
|
+
case 'window':
|
|
42
42
|
{
|
|
43
43
|
const { surface, ...view } = declaration;
|
|
44
44
|
return {
|
|
45
45
|
...view,
|
|
46
|
-
type:
|
|
46
|
+
type: 'app',
|
|
47
47
|
version
|
|
48
48
|
};
|
|
49
49
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/deployWorkbenchApp.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {createGzip} from 'node:zlib'\n\nimport {type AppVisibility, type CliConfig} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {pack} from 'tar-fs'\n\nimport {type DerivedInterface, deriveInterfaces} from '../../deriveInterfaces.js'\nimport {\n type Application,\n type BrettAccess,\n type BrettInterface,\n type BrettWorkspace,\n createApplication,\n createDeployment,\n deleteApplication,\n updateApplication,\n} from '../../services/applications.js'\n\n/**\n * `rollback` undoes the creation, so a later failure leaves no record stranded at the slug.\n * @internal\n */\nexport interface CreatedApplication {\n application: Application\n rollback: () => Promise<void>\n}\n\nfunction toBrettInterface(iface: DerivedInterface, version: string): BrettInterface {\n const {id: _id, src: _src, ...declaration} = iface\n if ('type' in declaration) return {...declaration, version}\n\n switch (declaration.surface) {\n case '
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/deployWorkbenchApp.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {createGzip} from 'node:zlib'\n\nimport {type AppVisibility, type CliConfig} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {pack} from 'tar-fs'\n\nimport {type DerivedInterface, deriveInterfaces} from '../../deriveInterfaces.js'\nimport {\n type Application,\n type BrettAccess,\n type BrettInterface,\n type BrettWorkspace,\n createApplication,\n createDeployment,\n deleteApplication,\n updateApplication,\n} from '../../services/applications.js'\n\n/**\n * `rollback` undoes the creation, so a later failure leaves no record stranded at the slug.\n * @internal\n */\nexport interface CreatedApplication {\n application: Application\n rollback: () => Promise<void>\n}\n\nfunction toBrettInterface(iface: DerivedInterface, version: string): BrettInterface {\n const {id: _id, src: _src, ...declaration} = iface\n if ('type' in declaration) return {...declaration, version}\n\n switch (declaration.surface) {\n case 'asset_source': {\n const {surface, ...view} = declaration\n return {...view, type: surface, version}\n }\n case 'panel': {\n const {surface, ...view} = declaration\n return {...view, type: surface, version}\n }\n case 'tile': {\n const {surface, ...view} = declaration\n return {...view, type: surface, version}\n }\n case 'window': {\n const {surface, ...view} = declaration\n return {...view, type: 'app', version}\n }\n }\n}\n\n/**\n * Create a coreApp record (no deployment), so the CLI can build with its id\n * before shipping the first deployment. First deploy only.\n * @internal\n */\nexport async function createCoreApp(options: {\n isSingleton?: boolean\n name?: string\n organizationId: string\n slug: string\n title: string\n visibility?: AppVisibility\n}): Promise<CreatedApplication> {\n const spin = spinner('Creating application...').start()\n try {\n const application = await createApplication({...options, type: 'coreApp'})\n spin.succeed()\n return {application, rollback: () => deleteApplication(application.id)}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n\n/**\n * Create a studio record (no deployment).\n * @internal\n */\nexport async function createStudio(options: {\n name?: string\n organizationId: string\n projectId: string | undefined\n slug: string\n title: string\n visibility?: AppVisibility\n}): Promise<CreatedApplication> {\n const spin = spinner('Creating studio...').start()\n try {\n const application = await createApplication({...options, type: 'studio'})\n spin.succeed()\n return {application, rollback: () => deleteApplication(application.id)}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n\n/**\n * Ship a deployment to an already-created (or `deployment.appId`) application,\n * then sync its mutable metadata (`title`, and `icon`/`visibility` when set)\n * from config. The deploy endpoint ignores these, so a redeploy patches them\n * here alongside the new deployment.\n *\n * `onDeployed` fires the instant the deployment is live, before the metadata\n * sync — so a caller can disarm a create-time rollback that must not delete an\n * application once it has an active deployment.\n * @internal\n */\nexport async function deployWorkbenchApp(options: {\n access?: readonly BrettAccess[]\n app: CliConfig['app']\n applicationId: string\n icon?: string\n isApp: boolean\n isAutoUpdating: boolean\n label?: string\n onDeployed?: () => void\n sourceDir: string\n title: string\n version: string\n visibility?: AppVisibility\n workspaces?: readonly BrettWorkspace[]\n}): Promise<void> {\n const {\n access,\n app,\n applicationId,\n icon,\n isApp,\n isAutoUpdating,\n label = 'Deploying...',\n onDeployed,\n sourceDir,\n title,\n version,\n visibility,\n workspaces,\n } = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner(label).start()\n try {\n await createDeployment({\n access,\n applicationId,\n // Brett assigns the id and resolves modules by `moduleId`, so neither travels.\n interfaces: deriveInterfaces(app, {appTitle: title, isApp}).map((iface) =>\n toBrettInterface(iface, version),\n ),\n isAutoUpdating,\n tarball,\n version,\n workspaces,\n })\n onDeployed?.()\n await updateApplication(applicationId, {\n title,\n ...(icon ? {icon} : {}),\n ...(visibility ? {visibility} : {}),\n })\n spin.succeed()\n } catch (error) {\n spin.clear()\n throw error\n }\n}\n"],"names":["basename","dirname","createGzip","spinner","pack","deriveInterfaces","createApplication","createDeployment","deleteApplication","updateApplication","toBrettInterface","iface","version","id","_id","src","_src","declaration","surface","view","type","createCoreApp","options","spin","start","application","succeed","rollback","error","fail","createStudio","deployWorkbenchApp","access","app","applicationId","icon","isApp","isAutoUpdating","label","onDeployed","sourceDir","title","visibility","workspaces","tarball","entries","pipe","interfaces","appTitle","map","clear"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/summarizeInterfaces.ts"],"sourcesContent":["import {type ServiceType, type ViewSurface} from '../../contract.js'\nimport {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\n\ninterface DeployedInterfaceBase {\n name: string\n src: string\n title: string\n}\n\n/** A view as the deploy report and `--json` output surface it. */\nexport interface DeployedView extends DeployedInterfaceBase {\n surface: ViewSurface\n}\n\n/** A web worker as the deploy report and `--json` output surface it. */\nexport interface DeployedWebWorker extends DeployedInterfaceBase {\n type: ServiceType\n}\n\nexport type DeployedInterface = DeployedView | DeployedWebWorker\n\nconst label = (item: {name: string; title: string}) =>\n item.title === item.name ? item.name : `${item.title} (${item.name})`\n\n/**\n * One `Title (name): src` report line per declared entry point.\n * @internal\n */\nexport function summarizeGroup(\n heading: string,\n items: readonly {name: string; src: string; title: string}[],\n): string {\n return `${heading}:\\n${items.map((item) => ` ${label(item)}: ${item.src}`).join('\\n')}`\n}\n\n/**\n * One report line per non-empty group, alongside the records `--json` reports.\n * @internal\n */\nexport function summarizeInterfaces({views, webWorkers}: WorkbenchExposes): {\n lines: string[]\n services: DeployedWebWorker[]\n views: DeployedView[]\n} {\n const deployedViews = (views ?? []).map(
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/summarizeInterfaces.ts"],"sourcesContent":["import {type ServiceType, type ViewSurface} from '../../contract.js'\nimport {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\n\ninterface DeployedInterfaceBase {\n name: string\n src: string\n title: string\n}\n\n/** A view as the deploy report and `--json` output surface it. */\nexport interface DeployedView extends DeployedInterfaceBase {\n surface: ViewSurface\n}\n\n/** A web worker as the deploy report and `--json` output surface it. */\nexport interface DeployedWebWorker extends DeployedInterfaceBase {\n type: ServiceType\n}\n\nexport type DeployedInterface = DeployedView | DeployedWebWorker\n\nconst label = (item: {name: string; title: string}) =>\n item.title === item.name ? item.name : `${item.title} (${item.name})`\n\n/**\n * One `Title (name): src` report line per declared entry point.\n * @internal\n */\nexport function summarizeGroup(\n heading: string,\n items: readonly {name: string; src: string; title: string}[],\n): string {\n return `${heading}:\\n${items.map((item) => ` ${label(item)}: ${item.src}`).join('\\n')}`\n}\n\n/**\n * One report line per non-empty group, alongside the records `--json` reports.\n * @internal\n */\nexport function summarizeInterfaces({views, webWorkers}: WorkbenchExposes): {\n lines: string[]\n services: DeployedWebWorker[]\n views: DeployedView[]\n} {\n const deployedViews = (views ?? []).map((view): DeployedView => ({\n name: view.name,\n src: view.src,\n surface: view.surface,\n title: view.title,\n }))\n const deployedServices = (webWorkers ?? []).map((webWorker): DeployedWebWorker => ({\n name: webWorker.name,\n src: webWorker.src,\n title: webWorker.title,\n type: webWorker.type,\n }))\n\n const lines: string[] = []\n if (deployedViews.length > 0) lines.push(summarizeGroup('Views', deployedViews))\n if (deployedServices.length > 0) lines.push(summarizeGroup('Web workers', deployedServices))\n return {lines, services: deployedServices, views: deployedViews}\n}\n"],"names":["label","item","title","name","summarizeGroup","heading","items","map","src","join","summarizeInterfaces","views","webWorkers","deployedViews","view","surface","deployedServices","webWorker","type","lines","length","push","services"],"mappings":"AAqBA,MAAMA,QAAQ,CAACC,OACbA,KAAKC,KAAK,KAAKD,KAAKE,IAAI,GAAGF,KAAKE,IAAI,GAAG,GAAGF,KAAKC,KAAK,CAAC,EAAE,EAAED,KAAKE,IAAI,CAAC,CAAC,CAAC;AAEvE;;;CAGC,GACD,OAAO,SAASC,eACdC,OAAe,EACfC,KAA4D;IAE5D,OAAO,GAAGD,QAAQ,GAAG,EAAEC,MAAMC,GAAG,CAAC,CAACN,OAAS,CAAC,EAAE,EAAED,MAAMC,MAAM,EAAE,EAAEA,KAAKO,GAAG,EAAE,EAAEC,IAAI,CAAC,OAAO;AAC1F;AAEA;;;CAGC,GACD,OAAO,SAASC,oBAAoB,EAACC,KAAK,EAAEC,UAAU,EAAmB;IAKvE,MAAMC,gBAAgB,AAACF,CAAAA,SAAS,EAAE,AAAD,EAAGJ,GAAG,CAAC,CAACO,OAAwB,CAAA;YAC/DX,MAAMW,KAAKX,IAAI;YACfK,KAAKM,KAAKN,GAAG;YACbO,SAASD,KAAKC,OAAO;YACrBb,OAAOY,KAAKZ,KAAK;QACnB,CAAA;IACA,MAAMc,mBAAmB,AAACJ,CAAAA,cAAc,EAAE,AAAD,EAAGL,GAAG,CAAC,CAACU,YAAkC,CAAA;YACjFd,MAAMc,UAAUd,IAAI;YACpBK,KAAKS,UAAUT,GAAG;YAClBN,OAAOe,UAAUf,KAAK;YACtBgB,MAAMD,UAAUC,IAAI;QACtB,CAAA;IAEA,MAAMC,QAAkB,EAAE;IAC1B,IAAIN,cAAcO,MAAM,GAAG,GAAGD,MAAME,IAAI,CAACjB,eAAe,SAASS;IACjE,IAAIG,iBAAiBI,MAAM,GAAG,GAAGD,MAAME,IAAI,CAACjB,eAAe,eAAeY;IAC1E,OAAO;QAACG;QAAOG,UAAUN;QAAkBL,OAAOE;IAAa;AACjE"}
|
|
@@ -3,7 +3,7 @@ const viewDeclarationSchema = z.looseObject({
|
|
|
3
3
|
name: z.string().check(z.regex(/^[a-zA-Z0-9_-]+$/, 'View `name` must match /^[a-zA-Z0-9_-]+$/')),
|
|
4
4
|
src: z.string(),
|
|
5
5
|
surface: z.enum([
|
|
6
|
-
'
|
|
6
|
+
'window',
|
|
7
7
|
'panel',
|
|
8
8
|
'asset_source',
|
|
9
9
|
'tile'
|
|
@@ -13,7 +13,7 @@ const viewDeclarationSchema = z.looseObject({
|
|
|
13
13
|
name: z.string().check(z.regex(/^[a-zA-Z0-9_-]+$/, 'View `name` must match /^[a-zA-Z0-9_-]+$/')),
|
|
14
14
|
src: z.string(),
|
|
15
15
|
type: z.enum([
|
|
16
|
-
'
|
|
16
|
+
'window',
|
|
17
17
|
'panel',
|
|
18
18
|
'asset_source',
|
|
19
19
|
'tile'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/viewDeployment.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\nconst viewDeclarationSchema = z.looseObject({\n name: z.string().check(z.regex(/^[a-zA-Z0-9_-]+$/, 'View `name` must match /^[a-zA-Z0-9_-]+$/')),\n src: z.string(),\n surface: z.enum(['
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/viewDeployment.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\nconst viewDeclarationSchema = z.looseObject({\n name: z.string().check(z.regex(/^[a-zA-Z0-9_-]+$/, 'View `name` must match /^[a-zA-Z0-9_-]+$/')),\n src: z.string(),\n surface: z.enum(['window', 'panel', 'asset_source', 'tile']),\n})\n\n/** A view record as persisted to the application service. */\nconst viewRecordSchema = z.looseObject({\n name: z.string().check(z.regex(/^[a-zA-Z0-9_-]+$/, 'View `name` must match /^[a-zA-Z0-9_-]+$/')),\n src: z.string(),\n type: z.enum(['window', 'panel', 'asset_source', 'tile']),\n})\n\n/**\n * Payload registering an app's views with the application service on deploy.\n *\n * Phase 1 stub: the service that stores views does not exist yet, so the\n * payload is validated and logged only — never sent. Builds the contract the\n * application-service endpoint will accept.\n */\nconst viewDeploymentPayloadSchema = z.object({\n applicationId: z.string(),\n views: z.array(viewRecordSchema),\n})\n\nexport type ViewDeploymentPayload = z.infer<typeof viewDeploymentPayloadSchema>\n\n/**\n * Validates an app's declared views into the application-service payload.\n * Throws (via Zod) when a view declaration is malformed.\n */\nexport function buildViewDeploymentPayload(input: {\n applicationId: string\n views?: ReadonlyArray<Record<string, unknown>>\n}): ViewDeploymentPayload {\n const views = z.array(viewDeclarationSchema).parse(input.views ?? [])\n return viewDeploymentPayloadSchema.parse({\n applicationId: input.applicationId,\n views: views.map(({surface, ...view}) => ({...view, type: surface})),\n })\n}\n"],"names":["z","viewDeclarationSchema","looseObject","name","string","check","regex","src","surface","enum","viewRecordSchema","type","viewDeploymentPayloadSchema","object","applicationId","views","array","buildViewDeploymentPayload","input","parse","map","view"],"mappings":"AAAA,SAAQA,CAAC,QAAO,WAAU;AAE1B,MAAMC,wBAAwBD,EAAEE,WAAW,CAAC;IAC1CC,MAAMH,EAAEI,MAAM,GAAGC,KAAK,CAACL,EAAEM,KAAK,CAAC,oBAAoB;IACnDC,KAAKP,EAAEI,MAAM;IACbI,SAASR,EAAES,IAAI,CAAC;QAAC;QAAU;QAAS;QAAgB;KAAO;AAC7D;AAEA,2DAA2D,GAC3D,MAAMC,mBAAmBV,EAAEE,WAAW,CAAC;IACrCC,MAAMH,EAAEI,MAAM,GAAGC,KAAK,CAACL,EAAEM,KAAK,CAAC,oBAAoB;IACnDC,KAAKP,EAAEI,MAAM;IACbO,MAAMX,EAAES,IAAI,CAAC;QAAC;QAAU;QAAS;QAAgB;KAAO;AAC1D;AAEA;;;;;;CAMC,GACD,MAAMG,8BAA8BZ,EAAEa,MAAM,CAAC;IAC3CC,eAAed,EAAEI,MAAM;IACvBW,OAAOf,EAAEgB,KAAK,CAACN;AACjB;AAIA;;;CAGC,GACD,OAAO,SAASO,2BAA2BC,KAG1C;IACC,MAAMH,QAAQf,EAAEgB,KAAK,CAACf,uBAAuBkB,KAAK,CAACD,MAAMH,KAAK,IAAI,EAAE;IACpE,OAAOH,4BAA4BO,KAAK,CAAC;QACvCL,eAAeI,MAAMJ,aAAa;QAClCC,OAAOA,MAAMK,GAAG,CAAC,CAAC,EAACZ,OAAO,EAAE,GAAGa,MAAK,GAAM,CAAA;gBAAC,GAAGA,IAAI;gBAAEV,MAAMH;YAAO,CAAA;IACnE;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/dev/appServerSupervisor.ts"],"sourcesContent":["import {type CliConfig, getCliConfigUncached} from '@sanity/cli-core'\nimport {type ViteDevServer} from 'vite'\n\n/**\n * Minimal shape the supervisor needs back from a started app/studio dev server.\n * The not-started arm is reserved for expected early exits the server already\n * reported (e.g. a missing organization id) — a failure to *boot* still throws.\n */\nexport type AppServerResult =\n | {close: () => Promise<void>; server: ViteDevServer; started: true}\n | {reason: string; started: false}\n\n/** Start the app/studio dev server for a given config (port + URL intent baked in by the caller). */\nexport type StartAppServer = (cliConfig: CliConfig) => Promise<AppServerResult>\n\nconst noop = async () => {}\n\nexport interface AppServerSupervisor {\n /** Stop the server once, waiting out any rebuild already in flight. */\n close: () => Promise<void>\n /** Tear down the running server and bring it back up with a freshly-loaded config. */\n rebuild: () => Promise<ViteDevServer>\n /** The currently-live dev server; re-points after a rebuild. */\n readonly server: ViteDevServer\n}\n\n/**\n * Own the app/studio dev server's lifecycle behind the dev-server registry seam.\n *\n * Adding or removing a view/service rebuilds the federation remote: its\n * module-federation `exposes` map and codegen artifacts are computed once at\n * server start, so a newly-declared interface has no expose until the server is\n * recreated — `server.restart()` can't do it (it reuses the inline config).\n * `rebuild` therefore tears the server down and starts a fresh one with a\n * reloaded config; the registry watcher calls it when the interface set changes.\n *\n * Returns the not-started result verbatim when the initial boot is an expected\n * early exit, so the caller can skip the rest of the orchestration.\n */\nexport async function startAppServerSupervisor(options: {\n cliConfig: CliConfig\n start: StartAppServer\n workDir: string\n}): Promise<{reason: string; started: false} | {started: true; supervisor: AppServerSupervisor}> {\n const {cliConfig, start, workDir} = options\n\n const initial = await start(cliConfig)\n if (!initial.started) return {reason: initial.reason, started: false}\n\n // `closeCurrent` repoints at the replacement only once a rebuild succeeds, so a\n // failed rebuild (old server already closed) leaves nothing for close() to re-close.\n let server = initial.server\n let closeCurrent = initial.close\n let closed = false\n // close() waits on this so a rebuild racing teardown can't orphan the replacement.\n // Rejections are the watcher's (warn + retry); the tracked copy is swallowed.\n let rebuildInFlight: Promise<unknown> = Promise.resolve()\n\n const runRebuild = async (): Promise<ViteDevServer> => {\n // Refuse once shutting down — a config save in the teardown window must not\n // boot a replacement nobody owns.\n if (closed) throw new Error('Dev server is shutting down')\n const freshConfig = await getCliConfigUncached(workDir)\n await closeCurrent()\n closeCurrent = noop\n const result = await start(freshConfig)\n if (!result.started) {\n // The server already reported why (e.g. organizationId was removed).\n throw new Error('Dev server did not restart after the view/service change')\n }\n server = result.server\n closeCurrent = result.close\n return server\n }\n\n return {\n started: true,\n supervisor: {\n async close() {\n closed = true\n await rebuildInFlight\n await closeCurrent()\n },\n rebuild() {\n const rebuild = runRebuild()\n rebuildInFlight = rebuild.catch(() => {})\n return rebuild\n },\n get server() {\n return server\n },\n },\n }\n}\n"],"names":["getCliConfigUncached","noop","startAppServerSupervisor","options","cliConfig","start","workDir","initial","started","reason","server","closeCurrent","close","closed","rebuildInFlight","Promise","resolve","runRebuild","Error","freshConfig","result","supervisor","rebuild","catch"],"mappings":"AAAA,SAAwBA,oBAAoB,QAAO,mBAAkB;AAerE,MAAMC,OAAO,WAAa;AAW1B;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeC,yBAAyBC,OAI9C;IACC,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,OAAO,EAAC,GAAGH;IAEpC,MAAMI,UAAU,MAAMF,MAAMD;IAC5B,IAAI,CAACG,QAAQC,OAAO,EAAE,OAAO;QAACC,QAAQF,QAAQE,MAAM;QAAED,SAAS;IAAK;IAEpE,gFAAgF;IAChF,qFAAqF;IACrF,IAAIE,SAASH,QAAQG,MAAM;IAC3B,IAAIC,eAAeJ,QAAQK,KAAK;IAChC,IAAIC,SAAS;IACb,mFAAmF;IACnF,8EAA8E;IAC9E,IAAIC,kBAAoCC,QAAQC,OAAO;IAEvD,MAAMC,aAAa;QACjB,4EAA4E;QAC5E,kCAAkC;QAClC,IAAIJ,QAAQ,MAAM,IAAIK,MAAM;QAC5B,MAAMC,cAAc,MAAMnB,qBAAqBM;QAC/C,MAAMK;QACNA,eAAeV;QACf,MAAMmB,SAAS,MAAMf,MAAMc;QAC3B,IAAI,CAACC,OAAOZ,OAAO,EAAE;YACnB,qEAAqE;YACrE,MAAM,IAAIU,MAAM;QAClB;QACAR,SAASU,OAAOV,MAAM;QACtBC,eAAeS,OAAOR,KAAK;QAC3B,OAAOF;IACT;IAEA,OAAO;QACLF,SAAS;QACTa,YAAY;YACV,MAAMT;gBACJC,SAAS;gBACT,MAAMC;gBACN,MAAMH;YACR;YACAW;gBACE,MAAMA,UAAUL;gBAChBH,kBAAkBQ,QAAQC,KAAK,CAAC,KAAO;gBACvC,OAAOD;YACT;YACA,IAAIZ
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/appServerSupervisor.ts"],"sourcesContent":["import {type CliConfig, getCliConfigUncached} from '@sanity/cli-core'\nimport {type ViteDevServer} from 'vite'\n\n/**\n * Minimal shape the supervisor needs back from a started app/studio dev server.\n * The not-started arm is reserved for expected early exits the server already\n * reported (e.g. a missing organization id) — a failure to *boot* still throws.\n */\nexport type AppServerResult =\n | {close: () => Promise<void>; server: ViteDevServer; started: true}\n | {reason: string; started: false}\n\n/** Start the app/studio dev server for a given config (port + URL intent baked in by the caller). */\nexport type StartAppServer = (cliConfig: CliConfig) => Promise<AppServerResult>\n\nconst noop = async () => {}\n\nexport interface AppServerSupervisor {\n /** Stop the server once, waiting out any rebuild already in flight. */\n close: () => Promise<void>\n /** Tear down the running server and bring it back up with a freshly-loaded config. */\n rebuild: () => Promise<ViteDevServer>\n /** The currently-live dev server; re-points after a rebuild. */\n readonly server: ViteDevServer\n}\n\n/**\n * Own the app/studio dev server's lifecycle behind the dev-server registry seam.\n *\n * Adding or removing a view/service rebuilds the federation remote: its\n * module-federation `exposes` map and codegen artifacts are computed once at\n * server start, so a newly-declared interface has no expose until the server is\n * recreated — `server.restart()` can't do it (it reuses the inline config).\n * `rebuild` therefore tears the server down and starts a fresh one with a\n * reloaded config; the registry watcher calls it when the interface set changes.\n *\n * Returns the not-started result verbatim when the initial boot is an expected\n * early exit, so the caller can skip the rest of the orchestration.\n */\nexport async function startAppServerSupervisor(options: {\n cliConfig: CliConfig\n start: StartAppServer\n workDir: string\n}): Promise<{reason: string; started: false} | {started: true; supervisor: AppServerSupervisor}> {\n const {cliConfig, start, workDir} = options\n\n const initial = await start(cliConfig)\n if (!initial.started) return {reason: initial.reason, started: false}\n\n // `closeCurrent` repoints at the replacement only once a rebuild succeeds, so a\n // failed rebuild (old server already closed) leaves nothing for close() to re-close.\n let server = initial.server\n let closeCurrent = initial.close\n let closed = false\n // close() waits on this so a rebuild racing teardown can't orphan the replacement.\n // Rejections are the watcher's (warn + retry); the tracked copy is swallowed.\n let rebuildInFlight: Promise<unknown> = Promise.resolve()\n\n const runRebuild = async (): Promise<ViteDevServer> => {\n // Refuse once shutting down — a config save in the teardown window must not\n // boot a replacement nobody owns.\n if (closed) throw new Error('Dev server is shutting down')\n const freshConfig = await getCliConfigUncached(workDir)\n await closeCurrent()\n closeCurrent = noop\n const result = await start(freshConfig)\n if (!result.started) {\n // The server already reported why (e.g. organizationId was removed).\n throw new Error('Dev server did not restart after the view/service change')\n }\n server = result.server\n closeCurrent = result.close\n return server\n }\n\n return {\n started: true,\n supervisor: {\n async close() {\n closed = true\n await rebuildInFlight\n await closeCurrent()\n },\n rebuild() {\n const rebuild = runRebuild()\n rebuildInFlight = rebuild.catch(() => {})\n return rebuild\n },\n get server() {\n return server\n },\n },\n }\n}\n"],"names":["getCliConfigUncached","noop","startAppServerSupervisor","options","cliConfig","start","workDir","initial","started","reason","server","closeCurrent","close","closed","rebuildInFlight","Promise","resolve","runRebuild","Error","freshConfig","result","supervisor","rebuild","catch"],"mappings":"AAAA,SAAwBA,oBAAoB,QAAO,mBAAkB;AAerE,MAAMC,OAAO,WAAa;AAW1B;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeC,yBAAyBC,OAI9C;IACC,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,OAAO,EAAC,GAAGH;IAEpC,MAAMI,UAAU,MAAMF,MAAMD;IAC5B,IAAI,CAACG,QAAQC,OAAO,EAAE,OAAO;QAACC,QAAQF,QAAQE,MAAM;QAAED,SAAS;IAAK;IAEpE,gFAAgF;IAChF,qFAAqF;IACrF,IAAIE,SAASH,QAAQG,MAAM;IAC3B,IAAIC,eAAeJ,QAAQK,KAAK;IAChC,IAAIC,SAAS;IACb,mFAAmF;IACnF,8EAA8E;IAC9E,IAAIC,kBAAoCC,QAAQC,OAAO;IAEvD,MAAMC,aAAa;QACjB,4EAA4E;QAC5E,kCAAkC;QAClC,IAAIJ,QAAQ,MAAM,IAAIK,MAAM;QAC5B,MAAMC,cAAc,MAAMnB,qBAAqBM;QAC/C,MAAMK;QACNA,eAAeV;QACf,MAAMmB,SAAS,MAAMf,MAAMc;QAC3B,IAAI,CAACC,OAAOZ,OAAO,EAAE;YACnB,qEAAqE;YACrE,MAAM,IAAIU,MAAM;QAClB;QACAR,SAASU,OAAOV,MAAM;QACtBC,eAAeS,OAAOR,KAAK;QAC3B,OAAOF;IACT;IAEA,OAAO;QACLF,SAAS;QACTa,YAAY;YACV,MAAMT;gBACJC,SAAS;gBACT,MAAMC;gBACN,MAAMH;YACR;YACAW;gBACE,MAAMA,UAAUL;gBAChBH,kBAAkBQ,QAAQC,KAAK,CAAC,KAAO;gBACvC,OAAOD;YACT;YACA,IAAIZ;gBACF,OAAOA;YACT;QACF;IACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/dev/registry.ts"],"sourcesContent":["import {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n unlinkSync,\n watch,\n writeFileSync,\n} from 'node:fs'\nimport {join} from 'node:path'\n\nimport {\n coreAppManifestSchema,\n getSanityDataDir,\n studioManifestSchema,\n subdebug,\n} from '@sanity/cli-core'\nimport {z} from 'zod/mini'\n\nimport {TileInterfaceMetadataSchema, ViewPlacementMetadataSchema} from '../../contract.js'\nimport {canonicalizeWatchDir} from './canonicalizeWatchDir.js'\nimport {getProcessStartTime, isOurProcess} from './processLiveness.js'\n\n/**\n * The dev-server registry: how a running `sanity dev` / `sanity start` process\n * advertises itself so the workbench on this machine can find and load it.\n *\n * Two kinds of file under `~/.sanity/dev-servers/` do the coordinating:\n *\n * - `<pid>.json` — one per running app/studio server, holding where it's served\n * plus its inlined manifest and interfaces. The workbench reads these to\n * discover and render local apps. Written by `registerDevServer`, watched by\n * `watchRegistry`.\n * - `workbench.lock` — a single machine-wide lock, so only one workbench shell\n * runs at a time and later `dev`s register into it instead of starting their\n * own. Managed by `acquireWorkbenchLock` / `readWorkbenchLock`.\n *\n * Both files belong to the process that created them and must not outlive it.\n * Three things keep that true: an explicit `release()` on clean shutdown, an\n * `exit` backstop for abrupt exits (`unlinkOnProcessExit`), and a dead-pid prune\n * on read (`isOurProcess`) that clears whatever a crashed process left behind.\n */\n\nconst devDebug = subdebug('dev')\n\n/** Bump when the manifest/lock shape changes in a breaking way. */\nconst REGISTRY_VERSION = 2\n\n/**\n * The current process's start time as reported by the OS, for the `startedAt`\n * that `isOurProcess` checks on re-read. Falls back to now when the OS time is\n * unavailable — `new Date()` alone records the write time, which drifts from\n * process start by enough to look stale and get pruned right after writing.\n */\nfunction ownStartedAt(): string {\n return (getProcessStartTime(process.pid) ?? new Date()).toISOString()\n}\n\nconst interfaceBaseFields = {\n /** CLI-minted for a local interface; a deployed one gets its id from Brett. */\n id: z.string(),\n moduleId: z.string(),\n name: z.string(),\n /** Raw source vite serves; a deployed interface carries only the `moduleId`. */\n src: z.string(),\n title: z.string(),\n version: z.optional(z.string()),\n}\n\n/**\n * A forwarded interface. Kept outside the manifest so\n * the workbench renders local panels and runs workers without a deploy.\n */\nconst devServerInterfaceSchema = z.union([\n z.discriminatedUnion('surface', [\n z.object({\n ...interfaceBaseFields,\n metadata: z.nullable(ViewPlacementMetadataSchema),\n surface: z.literal('app'),\n }),\n z.object({\n ...interfaceBaseFields,\n metadata: z.nullable(ViewPlacementMetadataSchema),\n surface: z.literal('panel'),\n }),\n z.object({...interfaceBaseFields, metadata: z.null(), surface: z.literal('asset_source')}),\n z.object({\n ...interfaceBaseFields,\n metadata: TileInterfaceMetadataSchema,\n surface: z.literal('tile'),\n }),\n ]),\n z.object({...interfaceBaseFields, metadata: z.null(), type: z.literal('worker')}),\n])\n\nconst devServerManifestSchema = z.object({\n /**\n * Field schema *values* load from the federation module; each field's `src`\n * rides along so a repoint bumps the exposes-set id and forces a rebuild.\n * Lenient — the workbench is the authority.\n */\n configs: z.optional(\n z.array(\n z.object({\n // Identifies the owning app when it has no app id (singletons).\n appType: z.optional(z.string()),\n fields: z.array(\n z.object({\n name: z.string(),\n public: z.optional(z.boolean()),\n src: z.string(),\n title: z.string(),\n }),\n ),\n // Content hash of the config — the workbench's change-detection key\n // (see deriveConfigs).\n id: z.string(),\n // The app's `defineApplication` slug — the module-federation alias the\n // workbench loads this config's live values from.\n moduleName: z.optional(z.string()),\n // The version the workbench federates this config's module under —\n // a string, like the one Brett returns on a deployed `activeConfig`.\n version: z.string(),\n }),\n ),\n ),\n host: z.string(),\n id: z.optional(z.string()),\n interfaces: z.optional(z.array(devServerInterfaceSchema)),\n /**\n * Inlined manifest — either a {@link StudioManifest} or {@link CoreAppManifest},\n * validated against the shared cli-core schemas. The registry stores and\n * rebroadcasts it; the CLI is what extracts and writes it.\n */\n manifest: z.optional(z.union([studioManifestSchema, coreAppManifestSchema])),\n /**\n * ISO timestamp of the most recent successful manifest extraction. Bumped\n * on every regeneration so re-writing this registry entry triggers the\n * workbench `watchRegistry` watcher and forces a rebroadcast to clients.\n */\n manifestUpdatedAt: z.optional(z.string()),\n // Stable identity + qualified reference, composed by the CLI (the authority for\n // local apps, which never reach brett) and read straight by the workbench.\n name: z.optional(z.string()),\n pid: z.number(),\n port: z.number(),\n projectId: z.optional(z.string()),\n reference: z.optional(z.string()),\n startedAt: z.string(),\n type: z.enum(['coreApp', 'studio']),\n version: z.literal(REGISTRY_VERSION),\n workDir: z.string(),\n})\n/**\n * A manifest describing a running dev server process (studio or app).\n * Stored as `~/.sanity/dev-servers/<pid>.json`.\n *\n * The workbench singleton is tracked separately via the lock file — see\n * `acquireWorkbenchLock` and `readWorkbenchLock` below.\n */\nexport type DevServerManifest = z.infer<typeof devServerManifestSchema>\n\n/**\n * A config-only server carries configs but no interfaces — e.g. a\n * media-library config app under development. The workbench never routes it\n * as an app; only its configs are published. It therefore plays a different\n * role than an app server, and the two may share a slug (a config app\n * developed alongside the locally served singleton it configures).\n */\nexport function isConfigOnlyServer(\n server: Pick<DevServerManifest, 'configs' | 'interfaces'>,\n): boolean {\n return Boolean(server.configs?.length) && !server.interfaces?.length\n}\n\n/**\n * Path to the dev server registry directory. Lives under the shared Sanity\n * config directory to stay consistent with other CLI paths.\n */\nfunction getRegistryDir(): string {\n return join(getSanityDataDir(), 'dev-servers')\n}\n\n// One shared `exit` listener drives every registered cleanup, so N locks/entries\n// don't each add a listener and trip Node's MaxListeners warning.\nconst exitCleanups = new Set<() => void>()\nlet exitListenerInstalled = false\n\nfunction runExitCleanups(): void {\n for (const cleanup of exitCleanups) cleanup()\n}\n\n/** Exercise the exit backstop in tests without terminating the process; not part\n * of the package's public surface. */\nexport const runRegistryExitCleanupForTesting = runExitCleanups\n\n/**\n * Delete a registry file synchronously on process exit, as a backstop for abrupt\n * termination. Vite installs its own SIGTERM handler that calls `process.exit()`,\n * which can outrun the async server teardown and leave the lock or registry entry\n * behind — a stray dev-server that lingers until the dead-pid prune clears it. The\n * `exit` event only runs synchronous work, hence `unlinkSync`. `ownedByUs` guards\n * the shared lock so a successor that reacquired it isn't wiped. Returns a\n * detacher to call after a clean release.\n */\nfunction unlinkOnProcessExit(filePath: string, ownedByUs: () => boolean): () => void {\n const cleanup = () => {\n if (!ownedByUs()) return\n try {\n unlinkSync(filePath)\n } catch {\n // The file may already have been removed during shutdown.\n }\n }\n exitCleanups.add(cleanup)\n\n if (!exitListenerInstalled) {\n exitListenerInstalled = true\n process.once('exit', runExitCleanups)\n }\n\n return () => exitCleanups.delete(cleanup)\n}\n\ninterface DevServerRegistration {\n /** Remove the registry entry. */\n release: () => void\n /**\n * Rewrite the registry entry with partial updates merged in. Also bumps the\n * file's mtime, which fires `watchRegistry` in any workbench process and\n * triggers a rebroadcast to connected clients.\n */\n update: (patch: Partial<Omit<DevServerManifest, 'pid' | 'startedAt' | 'version'>>) => void\n}\n\n/**\n * Write a manifest file for the current process and return a handle with a\n * `release` function that removes it plus an `update` function for patching\n * fields post-registration. Uses synchronous I/O so the file exists before\n * any signal handler could fire.\n */\nexport function registerDevServer(\n manifest: Omit<DevServerManifest, 'pid' | 'startedAt' | 'version'>,\n): DevServerRegistration {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n let current: DevServerManifest = {\n ...manifest,\n pid: process.pid,\n startedAt: ownStartedAt(),\n version: REGISTRY_VERSION,\n }\n\n const filePath = join(registryDir, `${process.pid}.json`)\n writeFileSync(filePath, JSON.stringify(current, null, 2))\n\n // Guard against late updates from background tasks (e.g. the initial\n // manifest extraction) landing after `release()` has deleted the file —\n // without this, the update would re-create the registry entry and leak.\n let released = false\n\n // The file is pid-named, so it's always ours to remove on exit.\n const detachExitCleanup = unlinkOnProcessExit(filePath, () => !released)\n\n return {\n release() {\n released = true\n detachExitCleanup()\n try {\n unlinkSync(filePath)\n } catch {\n // ENOENT is fine — already cleaned up\n }\n },\n update(patch) {\n if (released) return\n current = {...current, ...patch}\n writeFileSync(filePath, JSON.stringify(current, null, 2))\n },\n }\n}\n\n/**\n * Read all manifest files from the registry, prune stale entries (dead PIDs),\n * and return the live ones.\n */\nexport function getRegisteredServers(): DevServerManifest[] {\n const registryDir = getRegistryDir()\n\n if (!existsSync(registryDir)) {\n return []\n }\n\n const files = readdirSync(registryDir).filter((f) => f.endsWith('.json'))\n const servers: DevServerManifest[] = []\n\n for (const file of files) {\n const filePath = join(registryDir, file)\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(filePath, 'utf8'))\n } catch {\n continue\n }\n\n const {data, success} = devServerManifestSchema.safeParse(raw)\n if (!success) continue\n\n if (isOurProcess(data.pid, data.startedAt)) {\n servers.push(data)\n } else {\n try {\n unlinkSync(filePath)\n } catch {\n // Ignore — another process may have already cleaned it up\n }\n }\n }\n\n return servers\n}\n\ninterface RegistryWatcher {\n close(): void\n}\n\n/**\n * Watch the registry directory for changes and invoke the callback with the\n * current list of live servers whenever a change is detected.\n *\n * Uses `fs.watch` with a debounce to coalesce rapid file changes (e.g. a\n * server starting and writing its manifest triggers multiple FS events).\n */\nexport function watchRegistry(callback: (servers: DevServerManifest[]) => void): RegistryWatcher {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n // Canonicalize to the real long path so `fs.watch` doesn't abort on Windows\n // short-path dirs. See `canonicalizeWatchDir`.\n const watchDir = canonicalizeWatchDir(registryDir)\n\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\n\n const notify = () => {\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n callback(getRegisteredServers())\n }, 50)\n }\n\n const watcher = watch(watchDir, notify)\n\n return {\n close() {\n clearTimeout(debounceTimer)\n watcher.close()\n },\n }\n}\n\n// The workbench singleton lock — \"one workbench per machine\". Lives in the same\n// registry dir and shares the liveness/prune model: a stale lock left by a\n// crashed process is pruned on read so the next acquire isn't blocked forever.\n\nconst workbenchLockSchema = z.object({\n host: z.string(),\n pid: z.number(),\n port: z.number(),\n startedAt: z.string(),\n version: z.literal(REGISTRY_VERSION),\n})\n\n/**\n * Read the workbench lock file and return its contents if the holding\n * process is still alive. Prunes stale locks from crashed processes.\n */\nexport function readWorkbenchLock(): z.infer<typeof workbenchLockSchema> | undefined {\n const lockPath = join(getRegistryDir(), 'workbench.lock')\n\n let contents: string\n try {\n contents = readFileSync(lockPath, 'utf8')\n } catch {\n // File doesn't exist — nothing to prune, nothing to return\n return undefined\n }\n\n // Past this point the file exists. Anything that isn't a live, valid lock\n // (unparsable JSON, schema mismatch, dead/reused PID) is stale and must be\n // pruned — otherwise the next `acquireWorkbenchLock` call is blocked by\n // EEXIST forever and `sanity dev` silently no-ops the workbench server.\n const data = parseLockContents(contents)\n devDebug('Read workbench lock: %o', data)\n if (data && isOurProcess(data.pid, data.startedAt)) {\n devDebug('Workbench process is alive at pid %d on port %d', data.pid, data.port)\n return data\n }\n\n pruneWorkbenchLock(lockPath)\n return undefined\n}\n\nfunction parseLockContents(contents: string): z.infer<typeof workbenchLockSchema> | undefined {\n try {\n const {data, success} = workbenchLockSchema.safeParse(JSON.parse(contents))\n return success ? data : undefined\n } catch {\n return undefined\n }\n}\n\nfunction pruneWorkbenchLock(lockPath: string): void {\n try {\n devDebug('Removing stale workbench lock')\n unlinkSync(lockPath)\n devDebug('Stale workbench lock removed')\n } catch {\n // Another process may have already cleaned it up\n }\n}\n\ninterface WorkbenchLock {\n /** Release the lock file. */\n release: () => void\n /** Update the lock with the actual port after the server starts listening. */\n updatePort: (port: number) => void\n}\n\n/**\n * Attempt to acquire an exclusive lock for the workbench process.\n * Uses `O_EXCL` (the `wx` flag) which is atomic at the OS level — only one\n * process can create the file.\n *\n * The lock stores `{pid, host, port}` so other processes can find the\n * running workbench. Call `updatePort` after the Vite server starts to\n * write the actual port (Vite may pick a different one).\n *\n * @returns A {@link WorkbenchLock} if acquired, or `undefined` if another\n * live process already holds it.\n */\nexport function acquireWorkbenchLock(\n info: {host: string; port: number},\n retries = 1,\n): WorkbenchLock | undefined {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n const lockPath = join(registryDir, 'workbench.lock')\n const startedAt = ownStartedAt()\n const lockData = {\n host: info.host,\n pid: process.pid,\n port: info.port,\n startedAt,\n version: REGISTRY_VERSION,\n }\n\n devDebug('Acquiring workbench lock at %s', lockPath)\n\n try {\n writeFileSync(lockPath, JSON.stringify(lockData), {flag: 'wx'})\n devDebug('Workbench lock acquired')\n\n let released = false\n // Only wipe the lock on exit if it's still ours — a successor that reacquired\n // it after our own release must not be clobbered.\n const detachExitCleanup = unlinkOnProcessExit(lockPath, () => {\n if (released) return false\n try {\n const disk = parseLockContents(readFileSync(lockPath, 'utf8'))\n return disk?.pid === process.pid && disk.startedAt === startedAt\n } catch {\n return false\n }\n })\n\n return {\n release() {\n released = true\n detachExitCleanup()\n try {\n unlinkSync(lockPath)\n } catch {\n // Already cleaned up\n }\n },\n updatePort(port: number) {\n writeFileSync(lockPath, JSON.stringify({...lockData, port}))\n },\n }\n } catch (err: unknown) {\n devDebug(\n 'Failed to acquire workbench lock: %s',\n err instanceof Error ? err.message : String(err),\n )\n if (!isNodeError(err) || err.code !== 'EEXIST') return undefined\n\n // Lock exists — check if the holder is still alive\n const existing = readWorkbenchLock()\n if (existing) return undefined\n\n // Stale lock was pruned by readWorkbenchLock — retry (with guard against infinite recursion)\n if (retries <= 0) return undefined\n return acquireWorkbenchLock(info, retries - 1)\n }\n}\n\nfunction isNodeError(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && 'code' in err\n}\n"],"names":["existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","watch","writeFileSync","join","coreAppManifestSchema","getSanityDataDir","studioManifestSchema","subdebug","z","TileInterfaceMetadataSchema","ViewPlacementMetadataSchema","canonicalizeWatchDir","getProcessStartTime","isOurProcess","devDebug","REGISTRY_VERSION","ownStartedAt","process","pid","Date","toISOString","interfaceBaseFields","id","string","moduleId","name","src","title","version","optional","devServerInterfaceSchema","union","discriminatedUnion","object","metadata","nullable","surface","literal","null","type","devServerManifestSchema","configs","array","appType","fields","public","boolean","moduleName","host","interfaces","manifest","manifestUpdatedAt","number","port","projectId","reference","startedAt","enum","workDir","isConfigOnlyServer","server","Boolean","length","getRegistryDir","exitCleanups","Set","exitListenerInstalled","runExitCleanups","cleanup","runRegistryExitCleanupForTesting","unlinkOnProcessExit","filePath","ownedByUs","add","once","delete","registerDevServer","registryDir","recursive","current","JSON","stringify","released","detachExitCleanup","release","update","patch","getRegisteredServers","files","filter","f","endsWith","servers","file","raw","parse","data","success","safeParse","push","watchRegistry","callback","watchDir","debounceTimer","notify","clearTimeout","setTimeout","watcher","close","workbenchLockSchema","readWorkbenchLock","lockPath","contents","undefined","parseLockContents","pruneWorkbenchLock","acquireWorkbenchLock","info","retries","lockData","flag","disk","updatePort","err","Error","message","String","isNodeError","code","existing"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/registry.ts"],"sourcesContent":["import {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n unlinkSync,\n watch,\n writeFileSync,\n} from 'node:fs'\nimport {join} from 'node:path'\n\nimport {\n coreAppManifestSchema,\n getSanityDataDir,\n studioManifestSchema,\n subdebug,\n} from '@sanity/cli-core'\nimport {z} from 'zod/mini'\n\nimport {TileInterfaceMetadataSchema, ViewPlacementMetadataSchema} from '../../contract.js'\nimport {canonicalizeWatchDir} from './canonicalizeWatchDir.js'\nimport {getProcessStartTime, isOurProcess} from './processLiveness.js'\n\n/**\n * The dev-server registry: how a running `sanity dev` / `sanity start` process\n * advertises itself so the workbench on this machine can find and load it.\n *\n * Two kinds of file under `~/.sanity/dev-servers/` do the coordinating:\n *\n * - `<pid>.json` — one per running app/studio server, holding where it's served\n * plus its inlined manifest and interfaces. The workbench reads these to\n * discover and render local apps. Written by `registerDevServer`, watched by\n * `watchRegistry`.\n * - `workbench.lock` — a single machine-wide lock, so only one workbench shell\n * runs at a time and later `dev`s register into it instead of starting their\n * own. Managed by `acquireWorkbenchLock` / `readWorkbenchLock`.\n *\n * Both files belong to the process that created them and must not outlive it.\n * Three things keep that true: an explicit `release()` on clean shutdown, an\n * `exit` backstop for abrupt exits (`unlinkOnProcessExit`), and a dead-pid prune\n * on read (`isOurProcess`) that clears whatever a crashed process left behind.\n */\n\nconst devDebug = subdebug('dev')\n\n/** Bump when the manifest/lock shape changes in a breaking way. */\nconst REGISTRY_VERSION = 2\n\n/**\n * The current process's start time as reported by the OS, for the `startedAt`\n * that `isOurProcess` checks on re-read. Falls back to now when the OS time is\n * unavailable — `new Date()` alone records the write time, which drifts from\n * process start by enough to look stale and get pruned right after writing.\n */\nfunction ownStartedAt(): string {\n return (getProcessStartTime(process.pid) ?? new Date()).toISOString()\n}\n\nconst interfaceBaseFields = {\n /** CLI-minted for a local interface; a deployed one gets its id from Brett. */\n id: z.string(),\n moduleId: z.string(),\n name: z.string(),\n /** Raw source vite serves; a deployed interface carries only the `moduleId`. */\n src: z.string(),\n title: z.string(),\n version: z.optional(z.string()),\n}\n\n/**\n * A forwarded interface. Kept outside the manifest so\n * the workbench renders local panels and runs workers without a deploy.\n */\nconst devServerInterfaceSchema = z.union([\n z.discriminatedUnion('surface', [\n z.object({\n ...interfaceBaseFields,\n metadata: z.nullable(ViewPlacementMetadataSchema),\n surface: z.literal('window'),\n }),\n z.object({\n ...interfaceBaseFields,\n metadata: z.nullable(ViewPlacementMetadataSchema),\n surface: z.literal('panel'),\n }),\n z.object({...interfaceBaseFields, metadata: z.null(), surface: z.literal('asset_source')}),\n z.object({\n ...interfaceBaseFields,\n metadata: TileInterfaceMetadataSchema,\n surface: z.literal('tile'),\n }),\n ]),\n z.object({...interfaceBaseFields, metadata: z.null(), type: z.literal('worker')}),\n])\n\nconst devServerManifestSchema = z.object({\n /**\n * Field schema *values* load from the federation module; each field's `src`\n * rides along so a repoint bumps the exposes-set id and forces a rebuild.\n * Lenient — the workbench is the authority.\n */\n configs: z.optional(\n z.array(\n z.object({\n // Identifies the owning app when it has no app id (singletons).\n appType: z.optional(z.string()),\n fields: z.array(\n z.object({\n name: z.string(),\n public: z.optional(z.boolean()),\n src: z.string(),\n title: z.string(),\n }),\n ),\n // Content hash of the config — the workbench's change-detection key\n // (see deriveConfigs).\n id: z.string(),\n // The app's `defineApplication` slug — the module-federation alias the\n // workbench loads this config's live values from.\n moduleName: z.optional(z.string()),\n // The version the workbench federates this config's module under —\n // a string, like the one Brett returns on a deployed `activeConfig`.\n version: z.string(),\n }),\n ),\n ),\n host: z.string(),\n id: z.optional(z.string()),\n interfaces: z.optional(z.array(devServerInterfaceSchema)),\n /**\n * Inlined manifest — either a {@link StudioManifest} or {@link CoreAppManifest},\n * validated against the shared cli-core schemas. The registry stores and\n * rebroadcasts it; the CLI is what extracts and writes it.\n */\n manifest: z.optional(z.union([studioManifestSchema, coreAppManifestSchema])),\n /**\n * ISO timestamp of the most recent successful manifest extraction. Bumped\n * on every regeneration so re-writing this registry entry triggers the\n * workbench `watchRegistry` watcher and forces a rebroadcast to clients.\n */\n manifestUpdatedAt: z.optional(z.string()),\n // Stable identity + qualified reference, composed by the CLI (the authority for\n // local apps, which never reach brett) and read straight by the workbench.\n name: z.optional(z.string()),\n pid: z.number(),\n port: z.number(),\n projectId: z.optional(z.string()),\n reference: z.optional(z.string()),\n startedAt: z.string(),\n type: z.enum(['coreApp', 'studio']),\n version: z.literal(REGISTRY_VERSION),\n workDir: z.string(),\n})\n/**\n * A manifest describing a running dev server process (studio or app).\n * Stored as `~/.sanity/dev-servers/<pid>.json`.\n *\n * The workbench singleton is tracked separately via the lock file — see\n * `acquireWorkbenchLock` and `readWorkbenchLock` below.\n */\nexport type DevServerManifest = z.infer<typeof devServerManifestSchema>\n\n/**\n * A config-only server carries configs but no interfaces — e.g. a\n * media-library config app under development. The workbench never routes it\n * as an app; only its configs are published. It therefore plays a different\n * role than an app server, and the two may share a slug (a config app\n * developed alongside the locally served singleton it configures).\n */\nexport function isConfigOnlyServer(\n server: Pick<DevServerManifest, 'configs' | 'interfaces'>,\n): boolean {\n return Boolean(server.configs?.length) && !server.interfaces?.length\n}\n\n/**\n * Path to the dev server registry directory. Lives under the shared Sanity\n * config directory to stay consistent with other CLI paths.\n */\nfunction getRegistryDir(): string {\n return join(getSanityDataDir(), 'dev-servers')\n}\n\n// One shared `exit` listener drives every registered cleanup, so N locks/entries\n// don't each add a listener and trip Node's MaxListeners warning.\nconst exitCleanups = new Set<() => void>()\nlet exitListenerInstalled = false\n\nfunction runExitCleanups(): void {\n for (const cleanup of exitCleanups) cleanup()\n}\n\n/** Exercise the exit backstop in tests without terminating the process; not part\n * of the package's public surface. */\nexport const runRegistryExitCleanupForTesting = runExitCleanups\n\n/**\n * Delete a registry file synchronously on process exit, as a backstop for abrupt\n * termination. Vite installs its own SIGTERM handler that calls `process.exit()`,\n * which can outrun the async server teardown and leave the lock or registry entry\n * behind — a stray dev-server that lingers until the dead-pid prune clears it. The\n * `exit` event only runs synchronous work, hence `unlinkSync`. `ownedByUs` guards\n * the shared lock so a successor that reacquired it isn't wiped. Returns a\n * detacher to call after a clean release.\n */\nfunction unlinkOnProcessExit(filePath: string, ownedByUs: () => boolean): () => void {\n const cleanup = () => {\n if (!ownedByUs()) return\n try {\n unlinkSync(filePath)\n } catch {\n // The file may already have been removed during shutdown.\n }\n }\n exitCleanups.add(cleanup)\n\n if (!exitListenerInstalled) {\n exitListenerInstalled = true\n process.once('exit', runExitCleanups)\n }\n\n return () => exitCleanups.delete(cleanup)\n}\n\ninterface DevServerRegistration {\n /** Remove the registry entry. */\n release: () => void\n /**\n * Rewrite the registry entry with partial updates merged in. Also bumps the\n * file's mtime, which fires `watchRegistry` in any workbench process and\n * triggers a rebroadcast to connected clients.\n */\n update: (patch: Partial<Omit<DevServerManifest, 'pid' | 'startedAt' | 'version'>>) => void\n}\n\n/**\n * Write a manifest file for the current process and return a handle with a\n * `release` function that removes it plus an `update` function for patching\n * fields post-registration. Uses synchronous I/O so the file exists before\n * any signal handler could fire.\n */\nexport function registerDevServer(\n manifest: Omit<DevServerManifest, 'pid' | 'startedAt' | 'version'>,\n): DevServerRegistration {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n let current: DevServerManifest = {\n ...manifest,\n pid: process.pid,\n startedAt: ownStartedAt(),\n version: REGISTRY_VERSION,\n }\n\n const filePath = join(registryDir, `${process.pid}.json`)\n writeFileSync(filePath, JSON.stringify(current, null, 2))\n\n // Guard against late updates from background tasks (e.g. the initial\n // manifest extraction) landing after `release()` has deleted the file —\n // without this, the update would re-create the registry entry and leak.\n let released = false\n\n // The file is pid-named, so it's always ours to remove on exit.\n const detachExitCleanup = unlinkOnProcessExit(filePath, () => !released)\n\n return {\n release() {\n released = true\n detachExitCleanup()\n try {\n unlinkSync(filePath)\n } catch {\n // ENOENT is fine — already cleaned up\n }\n },\n update(patch) {\n if (released) return\n current = {...current, ...patch}\n writeFileSync(filePath, JSON.stringify(current, null, 2))\n },\n }\n}\n\n/**\n * Read all manifest files from the registry, prune stale entries (dead PIDs),\n * and return the live ones.\n */\nexport function getRegisteredServers(): DevServerManifest[] {\n const registryDir = getRegistryDir()\n\n if (!existsSync(registryDir)) {\n return []\n }\n\n const files = readdirSync(registryDir).filter((f) => f.endsWith('.json'))\n const servers: DevServerManifest[] = []\n\n for (const file of files) {\n const filePath = join(registryDir, file)\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(filePath, 'utf8'))\n } catch {\n continue\n }\n\n const {data, success} = devServerManifestSchema.safeParse(raw)\n if (!success) continue\n\n if (isOurProcess(data.pid, data.startedAt)) {\n servers.push(data)\n } else {\n try {\n unlinkSync(filePath)\n } catch {\n // Ignore — another process may have already cleaned it up\n }\n }\n }\n\n return servers\n}\n\ninterface RegistryWatcher {\n close(): void\n}\n\n/**\n * Watch the registry directory for changes and invoke the callback with the\n * current list of live servers whenever a change is detected.\n *\n * Uses `fs.watch` with a debounce to coalesce rapid file changes (e.g. a\n * server starting and writing its manifest triggers multiple FS events).\n */\nexport function watchRegistry(callback: (servers: DevServerManifest[]) => void): RegistryWatcher {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n // Canonicalize to the real long path so `fs.watch` doesn't abort on Windows\n // short-path dirs. See `canonicalizeWatchDir`.\n const watchDir = canonicalizeWatchDir(registryDir)\n\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\n\n const notify = () => {\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n callback(getRegisteredServers())\n }, 50)\n }\n\n const watcher = watch(watchDir, notify)\n\n return {\n close() {\n clearTimeout(debounceTimer)\n watcher.close()\n },\n }\n}\n\n// The workbench singleton lock — \"one workbench per machine\". Lives in the same\n// registry dir and shares the liveness/prune model: a stale lock left by a\n// crashed process is pruned on read so the next acquire isn't blocked forever.\n\nconst workbenchLockSchema = z.object({\n host: z.string(),\n pid: z.number(),\n port: z.number(),\n startedAt: z.string(),\n version: z.literal(REGISTRY_VERSION),\n})\n\n/**\n * Read the workbench lock file and return its contents if the holding\n * process is still alive. Prunes stale locks from crashed processes.\n */\nexport function readWorkbenchLock(): z.infer<typeof workbenchLockSchema> | undefined {\n const lockPath = join(getRegistryDir(), 'workbench.lock')\n\n let contents: string\n try {\n contents = readFileSync(lockPath, 'utf8')\n } catch {\n // File doesn't exist — nothing to prune, nothing to return\n return undefined\n }\n\n // Past this point the file exists. Anything that isn't a live, valid lock\n // (unparsable JSON, schema mismatch, dead/reused PID) is stale and must be\n // pruned — otherwise the next `acquireWorkbenchLock` call is blocked by\n // EEXIST forever and `sanity dev` silently no-ops the workbench server.\n const data = parseLockContents(contents)\n devDebug('Read workbench lock: %o', data)\n if (data && isOurProcess(data.pid, data.startedAt)) {\n devDebug('Workbench process is alive at pid %d on port %d', data.pid, data.port)\n return data\n }\n\n pruneWorkbenchLock(lockPath)\n return undefined\n}\n\nfunction parseLockContents(contents: string): z.infer<typeof workbenchLockSchema> | undefined {\n try {\n const {data, success} = workbenchLockSchema.safeParse(JSON.parse(contents))\n return success ? data : undefined\n } catch {\n return undefined\n }\n}\n\nfunction pruneWorkbenchLock(lockPath: string): void {\n try {\n devDebug('Removing stale workbench lock')\n unlinkSync(lockPath)\n devDebug('Stale workbench lock removed')\n } catch {\n // Another process may have already cleaned it up\n }\n}\n\ninterface WorkbenchLock {\n /** Release the lock file. */\n release: () => void\n /** Update the lock with the actual port after the server starts listening. */\n updatePort: (port: number) => void\n}\n\n/**\n * Attempt to acquire an exclusive lock for the workbench process.\n * Uses `O_EXCL` (the `wx` flag) which is atomic at the OS level — only one\n * process can create the file.\n *\n * The lock stores `{pid, host, port}` so other processes can find the\n * running workbench. Call `updatePort` after the Vite server starts to\n * write the actual port (Vite may pick a different one).\n *\n * @returns A {@link WorkbenchLock} if acquired, or `undefined` if another\n * live process already holds it.\n */\nexport function acquireWorkbenchLock(\n info: {host: string; port: number},\n retries = 1,\n): WorkbenchLock | undefined {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n const lockPath = join(registryDir, 'workbench.lock')\n const startedAt = ownStartedAt()\n const lockData = {\n host: info.host,\n pid: process.pid,\n port: info.port,\n startedAt,\n version: REGISTRY_VERSION,\n }\n\n devDebug('Acquiring workbench lock at %s', lockPath)\n\n try {\n writeFileSync(lockPath, JSON.stringify(lockData), {flag: 'wx'})\n devDebug('Workbench lock acquired')\n\n let released = false\n // Only wipe the lock on exit if it's still ours — a successor that reacquired\n // it after our own release must not be clobbered.\n const detachExitCleanup = unlinkOnProcessExit(lockPath, () => {\n if (released) return false\n try {\n const disk = parseLockContents(readFileSync(lockPath, 'utf8'))\n return disk?.pid === process.pid && disk.startedAt === startedAt\n } catch {\n return false\n }\n })\n\n return {\n release() {\n released = true\n detachExitCleanup()\n try {\n unlinkSync(lockPath)\n } catch {\n // Already cleaned up\n }\n },\n updatePort(port: number) {\n writeFileSync(lockPath, JSON.stringify({...lockData, port}))\n },\n }\n } catch (err: unknown) {\n devDebug(\n 'Failed to acquire workbench lock: %s',\n err instanceof Error ? err.message : String(err),\n )\n if (!isNodeError(err) || err.code !== 'EEXIST') return undefined\n\n // Lock exists — check if the holder is still alive\n const existing = readWorkbenchLock()\n if (existing) return undefined\n\n // Stale lock was pruned by readWorkbenchLock — retry (with guard against infinite recursion)\n if (retries <= 0) return undefined\n return acquireWorkbenchLock(info, retries - 1)\n }\n}\n\nfunction isNodeError(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && 'code' in err\n}\n"],"names":["existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","watch","writeFileSync","join","coreAppManifestSchema","getSanityDataDir","studioManifestSchema","subdebug","z","TileInterfaceMetadataSchema","ViewPlacementMetadataSchema","canonicalizeWatchDir","getProcessStartTime","isOurProcess","devDebug","REGISTRY_VERSION","ownStartedAt","process","pid","Date","toISOString","interfaceBaseFields","id","string","moduleId","name","src","title","version","optional","devServerInterfaceSchema","union","discriminatedUnion","object","metadata","nullable","surface","literal","null","type","devServerManifestSchema","configs","array","appType","fields","public","boolean","moduleName","host","interfaces","manifest","manifestUpdatedAt","number","port","projectId","reference","startedAt","enum","workDir","isConfigOnlyServer","server","Boolean","length","getRegistryDir","exitCleanups","Set","exitListenerInstalled","runExitCleanups","cleanup","runRegistryExitCleanupForTesting","unlinkOnProcessExit","filePath","ownedByUs","add","once","delete","registerDevServer","registryDir","recursive","current","JSON","stringify","released","detachExitCleanup","release","update","patch","getRegisteredServers","files","filter","f","endsWith","servers","file","raw","parse","data","success","safeParse","push","watchRegistry","callback","watchDir","debounceTimer","notify","clearTimeout","setTimeout","watcher","close","workbenchLockSchema","readWorkbenchLock","lockPath","contents","undefined","parseLockContents","pruneWorkbenchLock","acquireWorkbenchLock","info","retries","lockData","flag","disk","updatePort","err","Error","message","String","isNodeError","code","existing"],"mappings":"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"}
|
package/dist/contract.js
CHANGED
|
@@ -6,7 +6,6 @@ import { z } from 'zod/mini';
|
|
|
6
6
|
/** @internal */ export const SERVICE_CONTRACT_VERSION = 1;
|
|
7
7
|
/** @internal */ export const MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION = 1;
|
|
8
8
|
/** Component slots per view surface; windows load outside the component artifact path. @internal */ export const VIEW_COMPONENTS = {
|
|
9
|
-
app: [],
|
|
10
9
|
asset_source: [
|
|
11
10
|
'asset_source'
|
|
12
11
|
],
|
|
@@ -16,7 +15,8 @@ import { z } from 'zod/mini';
|
|
|
16
15
|
],
|
|
17
16
|
tile: [
|
|
18
17
|
'tile'
|
|
19
|
-
]
|
|
18
|
+
],
|
|
19
|
+
window: []
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
22
|
* A tile's footprint family — the shape it occupies on the dashboard. Modelled
|
|
@@ -55,10 +55,10 @@ const DockGroupSchema = z.enum([
|
|
|
55
55
|
* renders/runs what it expects.
|
|
56
56
|
* @internal
|
|
57
57
|
*/ const INTERFACE_CONTRACT_VERSIONS = {
|
|
58
|
-
app: undefined,
|
|
59
58
|
asset_source: VIEW_CONTRACT_VERSION,
|
|
60
59
|
panel: VIEW_CONTRACT_VERSION,
|
|
61
60
|
tile: VIEW_CONTRACT_VERSION,
|
|
61
|
+
window: undefined,
|
|
62
62
|
worker: SERVICE_CONTRACT_VERSION
|
|
63
63
|
};
|
|
64
64
|
/** @internal */ export function interfaceContractVersion(kind) {
|
|
@@ -71,16 +71,16 @@ const DockGroupSchema = z.enum([
|
|
|
71
71
|
* @internal
|
|
72
72
|
*/ export function interfaceModuleId(kind, name) {
|
|
73
73
|
switch(kind){
|
|
74
|
-
case 'app':
|
|
75
|
-
{
|
|
76
|
-
return 'App';
|
|
77
|
-
}
|
|
78
74
|
case 'asset_source':
|
|
79
75
|
case 'panel':
|
|
80
76
|
case 'tile':
|
|
81
77
|
{
|
|
82
78
|
return `views/${name}`;
|
|
83
79
|
}
|
|
80
|
+
case 'window':
|
|
81
|
+
{
|
|
82
|
+
return 'App';
|
|
83
|
+
}
|
|
84
84
|
case 'worker':
|
|
85
85
|
{
|
|
86
86
|
return `services/${name}`;
|
|
@@ -119,14 +119,14 @@ const PanelViewSchema = z.object({
|
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
121
|
const WindowViewSchema = z.object({
|
|
122
|
-
surface: z.literal('
|
|
122
|
+
surface: z.literal('window'),
|
|
123
123
|
...interfaceDeclarationFields('View'),
|
|
124
124
|
dock: z.optional(DockSchema)
|
|
125
125
|
});
|
|
126
126
|
/** @public */ export function defineWindowView(view) {
|
|
127
127
|
return {
|
|
128
128
|
...view,
|
|
129
|
-
surface: '
|
|
129
|
+
surface: 'window'
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
const AssetSourceViewSchema = z.object({
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/contract.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\n// Shared module-federation extension contract: interface (view/service) and\n// config declaration schemas, plus the versions the build stamps.\n// `zod/mini` keeps the bundle small.\n\n/** @internal */\nexport const VIEW_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const SERVICE_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION = 1\n\n/**\n * A view component. The return is opaque so the runtime helpers carry no React\n * dependency — the generated artifact renders it with the app's own React.\n * @public\n */\nexport type ViewComponent<TProps> = (props: TProps) => unknown\n\n/** @public */\nexport interface ViewComponentBaseProps<TView> {\n view: TView\n}\n\n/** Component slots per view surface; windows load outside the component artifact path. @internal */\nexport const VIEW_COMPONENTS = {\n app: [],\n asset_source: ['asset_source'],\n panel: ['title', 'panel'],\n tile: ['tile'],\n} as const satisfies Record<string, readonly string[]>\n\n/** @public */\nexport type ViewSurface = keyof typeof VIEW_COMPONENTS\n\n/**\n * A tile's footprint family — the shape it occupies on the dashboard. Modelled\n * on iOS WidgetKit families, not a linear scale: `banner` is full-width and\n * shallow, which a `small`→`large` magnitude can't express. The host maps a\n * family to a layout slot; the component reads it to render per footprint.\n * @public\n */\nexport const TileSizeSchema = z.enum(['small', 'large', 'banner'])\n\n/** @public */\nexport type TileSize = z.infer<typeof TileSizeSchema>\n\n/** @public */\nexport type ServiceType = 'worker'\n\nconst DockGroupSchema = z.enum(['dock.system', 'dock.applications', 'dock.user'])\n\n/** @public */\nexport type DockGroup = z.output<typeof DockGroupSchema>\n\n/** @internal */\nexport const DockSchema = z.object({\n group: z.optional(DockGroupSchema),\n order: z.optional(z.number()),\n})\n\n/** @internal */\nexport type Dock = z.infer<typeof DockSchema>\n\n/** @internal */\nexport const ViewPlacementMetadataSchema = z.object({dock: DockSchema})\n\n/** @internal */\nexport type ViewPlacementMetadata = z.infer<typeof ViewPlacementMetadataSchema>\n\n/**\n * A tile's interface metadata: its footprint `size` and an optional `order`\n * the dashboard sorts on, ascending. Both are authored as top-level view fields\n * (see {@link InterfaceDeclarationSchema}) but stored on the record as metadata.\n * @internal\n */\nexport const TileInterfaceMetadataSchema = z.object({\n order: z.optional(z.number()),\n size: TileSizeSchema,\n})\n\n/** @internal */\nexport type TileInterfaceMetadata = z.infer<typeof TileInterfaceMetadataSchema>\n\n/**\n * The contract version each interface type advertises, so the host can check it\n * renders/runs what it expects.\n * @internal\n */\nconst INTERFACE_CONTRACT_VERSIONS = {\n app: undefined,\n asset_source: VIEW_CONTRACT_VERSION,\n panel: VIEW_CONTRACT_VERSION,\n tile: VIEW_CONTRACT_VERSION,\n worker: SERVICE_CONTRACT_VERSION,\n} as const\n\n/** Every interface type an app exposes — an `app` view, a view, or a service. */\nexport type InterfaceKind = keyof typeof INTERFACE_CONTRACT_VERSIONS\n\n/** @internal */\nexport function interfaceContractVersion(kind: InterfaceKind): string | undefined {\n const version = INTERFACE_CONTRACT_VERSIONS[kind]\n return version === undefined ? undefined : String(version)\n}\n\n/**\n * The module-federation id a build exposes an interface at. Dev stamps the same\n * id a deploy would, so the workbench loads a local interface like a deployed one.\n * @internal\n */\nexport function interfaceModuleId(kind: string, name: string): string {\n switch (kind) {\n case 'app': {\n return 'App'\n }\n case 'asset_source':\n case 'panel':\n case 'tile': {\n return `views/${name}`\n }\n case 'worker': {\n return `services/${name}`\n }\n default: {\n throw new Error(`Cannot derive a moduleId for unknown interface kind: ${kind}`)\n }\n }\n}\n\n// Shared `name` + `src`; `kind` only tailors the validation message.\nfunction extensionDeclarationFields(kind: 'Field' | 'View' | 'Web worker') {\n const pattern = /^[a-zA-Z0-9_-]+$/\n return {\n name: z.string().check(z.regex(pattern, `${kind} \\`name\\` must match ${pattern}`)),\n src: z.string(),\n }\n}\n\n// Every interface shares `name` + `src` + a display `title`,\n// which Brett requires on the record each becomes.\nfunction interfaceDeclarationFields(kind: 'View' | 'Web worker') {\n return {\n ...extensionDeclarationFields(kind),\n title: z.string(`${kind} \\`title\\` is required`),\n }\n}\n\nconst PanelViewSchema = z.object({\n surface: z.literal('panel'),\n ...interfaceDeclarationFields('View'),\n dock: z.optional(DockSchema),\n})\n\n/** @public */\nexport type PanelView = z.output<typeof PanelViewSchema>\n\n/** @public */\nexport type DefinePanelViewInput = Omit<PanelView, 'surface'>\n\n/** @public */\nexport function definePanelView(view: DefinePanelViewInput): PanelView {\n return {...view, surface: 'panel'}\n}\n\nconst WindowViewSchema = z.object({\n surface: z.literal('app'),\n ...interfaceDeclarationFields('View'),\n dock: z.optional(DockSchema),\n})\n\n/** @public */\nexport type WindowView = z.output<typeof WindowViewSchema>\n\n/** @public */\nexport type DefineWindowViewInput = Omit<WindowView, 'surface'>\n\n/** @public */\nexport function defineWindowView(view: DefineWindowViewInput): WindowView {\n return {...view, surface: 'app'}\n}\n\nconst AssetSourceViewSchema = z.object({\n surface: z.literal('asset_source'),\n ...interfaceDeclarationFields('View'),\n})\n\n/** @public */\nexport type AssetSourceView = z.output<typeof AssetSourceViewSchema>\n\n/** @public */\nexport type DefineAssetSourceViewInput = Omit<AssetSourceView, 'surface'>\n\n/** @public */\nexport function defineAssetSourceView(view: DefineAssetSourceViewInput): AssetSourceView {\n return {...view, surface: 'asset_source'}\n}\n\nconst TileViewSchema = z.object({\n surface: z.literal('tile'),\n ...interfaceDeclarationFields('View'),\n /** Sort position within its layout track, ascending. Optional. */\n order: z.optional(z.number()),\n /** Footprint family the dashboard lays the tile out by. */\n size: TileSizeSchema,\n})\n\n/** @public */\nexport type TileView = z.output<typeof TileViewSchema>\n\n/** @public */\nexport type DefineTileViewInput = Omit<TileView, 'surface'>\n\n/** @public */\nexport function defineTileView(view: DefineTileViewInput): TileView {\n return {...view, surface: 'tile'}\n}\n\n/** @internal */\nexport const InterfaceDeclarationSchema = z.discriminatedUnion('surface', [\n WindowViewSchema,\n PanelViewSchema,\n AssetSourceViewSchema,\n TileViewSchema,\n])\n\n/** @public */\nexport type ViewDeclaration = z.output<typeof InterfaceDeclarationSchema>\n\nconst WebWorkerSchema = z.object({\n type: z.literal('worker'),\n ...interfaceDeclarationFields('Web worker'),\n})\n\n/** @public */\nexport type WebWorker = z.output<typeof WebWorkerSchema>\n\n/** @public */\nexport type DefineWebWorkerInput = Omit<WebWorker, 'type'>\n\n/** @public */\nexport function defineWebWorker(webWorker: DefineWebWorkerInput): WebWorker {\n return {...webWorker, type: 'worker'}\n}\n\n/** @internal */\nexport const ServiceDeclarationSchema = z.discriminatedUnion('type', [WebWorkerSchema])\n\nconst MediaLibraryFieldSchema = z.object({\n ...extensionDeclarationFields('Field'),\n public: z.optional(z.boolean()),\n title: z.string(),\n})\n\n/**\n * Stamped where the config crosses a boundary so the authoring model doesn't carry a constant discriminator.\n * @internal\n */\nexport const INSTALLATION_CONFIG_TYPE = 'installation_config'\n\n// `appType` is stamped by `unstable_defineMediaLibrary`, never authored.\nconst MediaLibraryConfigSchema = z.object({\n appType: z.literal('media-library'),\n fields: z\n .array(MediaLibraryFieldSchema)\n .check(\n z.refine(\n (fields) => new Set(fields.map((field) => field.name)).size === fields.length,\n 'Field `name` must be unique within a media library',\n ),\n ),\n})\n\n/**\n * A workbench config's built value, keyed by `appType`; deploys as a versioned\n * snapshot, not an interface.\n * @internal\n */\nexport const ConfigSchema = z.discriminatedUnion('appType', [MediaLibraryConfigSchema])\n\n/**\n * The `{appType, fields}` config value the build expands into a federation\n * remote and the deploy summarizes.\n * @internal\n */\nexport type WorkbenchConfigValue = z.output<typeof ConfigSchema>\n"],"names":["z","VIEW_CONTRACT_VERSION","SERVICE_CONTRACT_VERSION","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","VIEW_COMPONENTS","app","asset_source","panel","tile","TileSizeSchema","enum","DockGroupSchema","DockSchema","object","group","optional","order","number","ViewPlacementMetadataSchema","dock","TileInterfaceMetadataSchema","size","INTERFACE_CONTRACT_VERSIONS","undefined","worker","interfaceContractVersion","kind","version","String","interfaceModuleId","name","Error","extensionDeclarationFields","pattern","string","check","regex","src","interfaceDeclarationFields","title","PanelViewSchema","surface","literal","definePanelView","view","WindowViewSchema","defineWindowView","AssetSourceViewSchema","defineAssetSourceView","TileViewSchema","defineTileView","InterfaceDeclarationSchema","discriminatedUnion","WebWorkerSchema","type","defineWebWorker","webWorker","ServiceDeclarationSchema","MediaLibraryFieldSchema","public","boolean","INSTALLATION_CONFIG_TYPE","MediaLibraryConfigSchema","appType","fields","array","refine","Set","map","field","length","ConfigSchema"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/contract.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\n// Shared module-federation extension contract: interface (view/service) and\n// config declaration schemas, plus the versions the build stamps.\n// `zod/mini` keeps the bundle small.\n\n/** @internal */\nexport const VIEW_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const SERVICE_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION = 1\n\n/**\n * A view component. The return is opaque so the runtime helpers carry no React\n * dependency — the generated artifact renders it with the app's own React.\n * @public\n */\nexport type ViewComponent<TProps> = (props: TProps) => unknown\n\n/** @public */\nexport interface ViewComponentBaseProps<TView> {\n view: TView\n}\n\n/** Component slots per view surface; windows load outside the component artifact path. @internal */\nexport const VIEW_COMPONENTS = {\n asset_source: ['asset_source'],\n panel: ['title', 'panel'],\n tile: ['tile'],\n window: [],\n} as const satisfies Record<string, readonly string[]>\n\n/** @public */\nexport type ViewSurface = keyof typeof VIEW_COMPONENTS\n\n/**\n * A tile's footprint family — the shape it occupies on the dashboard. Modelled\n * on iOS WidgetKit families, not a linear scale: `banner` is full-width and\n * shallow, which a `small`→`large` magnitude can't express. The host maps a\n * family to a layout slot; the component reads it to render per footprint.\n * @public\n */\nexport const TileSizeSchema = z.enum(['small', 'large', 'banner'])\n\n/** @public */\nexport type TileSize = z.infer<typeof TileSizeSchema>\n\n/** @public */\nexport type ServiceType = 'worker'\n\nconst DockGroupSchema = z.enum(['dock.system', 'dock.applications', 'dock.user'])\n\n/** @public */\nexport type DockGroup = z.output<typeof DockGroupSchema>\n\n/** @internal */\nexport const DockSchema = z.object({\n group: z.optional(DockGroupSchema),\n order: z.optional(z.number()),\n})\n\n/** @internal */\nexport type Dock = z.infer<typeof DockSchema>\n\n/** @internal */\nexport const ViewPlacementMetadataSchema = z.object({dock: DockSchema})\n\n/** @internal */\nexport type ViewPlacementMetadata = z.infer<typeof ViewPlacementMetadataSchema>\n\n/**\n * A tile's interface metadata: its footprint `size` and an optional `order`\n * the dashboard sorts on, ascending. Both are authored as top-level view fields\n * (see {@link InterfaceDeclarationSchema}) but stored on the record as metadata.\n * @internal\n */\nexport const TileInterfaceMetadataSchema = z.object({\n order: z.optional(z.number()),\n size: TileSizeSchema,\n})\n\n/** @internal */\nexport type TileInterfaceMetadata = z.infer<typeof TileInterfaceMetadataSchema>\n\n/**\n * The contract version each interface type advertises, so the host can check it\n * renders/runs what it expects.\n * @internal\n */\nconst INTERFACE_CONTRACT_VERSIONS = {\n asset_source: VIEW_CONTRACT_VERSION,\n panel: VIEW_CONTRACT_VERSION,\n tile: VIEW_CONTRACT_VERSION,\n window: undefined,\n worker: SERVICE_CONTRACT_VERSION,\n} as const\n\n/** Every interface type an app exposes — a window, a view, or a service. */\nexport type InterfaceKind = keyof typeof INTERFACE_CONTRACT_VERSIONS\n\n/** @internal */\nexport function interfaceContractVersion(kind: InterfaceKind): string | undefined {\n const version = INTERFACE_CONTRACT_VERSIONS[kind]\n return version === undefined ? undefined : String(version)\n}\n\n/**\n * The module-federation id a build exposes an interface at. Dev stamps the same\n * id a deploy would, so the workbench loads a local interface like a deployed one.\n * @internal\n */\nexport function interfaceModuleId(kind: string, name: string): string {\n switch (kind) {\n case 'asset_source':\n case 'panel':\n case 'tile': {\n return `views/${name}`\n }\n case 'window': {\n return 'App'\n }\n case 'worker': {\n return `services/${name}`\n }\n default: {\n throw new Error(`Cannot derive a moduleId for unknown interface kind: ${kind}`)\n }\n }\n}\n\n// Shared `name` + `src`; `kind` only tailors the validation message.\nfunction extensionDeclarationFields(kind: 'Field' | 'View' | 'Web worker') {\n const pattern = /^[a-zA-Z0-9_-]+$/\n return {\n name: z.string().check(z.regex(pattern, `${kind} \\`name\\` must match ${pattern}`)),\n src: z.string(),\n }\n}\n\n// Every interface shares `name` + `src` + a display `title`,\n// which Brett requires on the record each becomes.\nfunction interfaceDeclarationFields(kind: 'View' | 'Web worker') {\n return {\n ...extensionDeclarationFields(kind),\n title: z.string(`${kind} \\`title\\` is required`),\n }\n}\n\nconst PanelViewSchema = z.object({\n surface: z.literal('panel'),\n ...interfaceDeclarationFields('View'),\n dock: z.optional(DockSchema),\n})\n\n/** @public */\nexport type PanelView = z.output<typeof PanelViewSchema>\n\n/** @public */\nexport type DefinePanelViewInput = Omit<PanelView, 'surface'>\n\n/** @public */\nexport function definePanelView(view: DefinePanelViewInput): PanelView {\n return {...view, surface: 'panel'}\n}\n\nconst WindowViewSchema = z.object({\n surface: z.literal('window'),\n ...interfaceDeclarationFields('View'),\n dock: z.optional(DockSchema),\n})\n\n/** @public */\nexport type WindowView = z.output<typeof WindowViewSchema>\n\n/** @public */\nexport type DefineWindowViewInput = Omit<WindowView, 'surface'>\n\n/** @public */\nexport function defineWindowView(view: DefineWindowViewInput): WindowView {\n return {...view, surface: 'window'}\n}\n\nconst AssetSourceViewSchema = z.object({\n surface: z.literal('asset_source'),\n ...interfaceDeclarationFields('View'),\n})\n\n/** @public */\nexport type AssetSourceView = z.output<typeof AssetSourceViewSchema>\n\n/** @public */\nexport type DefineAssetSourceViewInput = Omit<AssetSourceView, 'surface'>\n\n/** @public */\nexport function defineAssetSourceView(view: DefineAssetSourceViewInput): AssetSourceView {\n return {...view, surface: 'asset_source'}\n}\n\nconst TileViewSchema = z.object({\n surface: z.literal('tile'),\n ...interfaceDeclarationFields('View'),\n /** Sort position within its layout track, ascending. Optional. */\n order: z.optional(z.number()),\n /** Footprint family the dashboard lays the tile out by. */\n size: TileSizeSchema,\n})\n\n/** @public */\nexport type TileView = z.output<typeof TileViewSchema>\n\n/** @public */\nexport type DefineTileViewInput = Omit<TileView, 'surface'>\n\n/** @public */\nexport function defineTileView(view: DefineTileViewInput): TileView {\n return {...view, surface: 'tile'}\n}\n\n/** @internal */\nexport const InterfaceDeclarationSchema = z.discriminatedUnion('surface', [\n WindowViewSchema,\n PanelViewSchema,\n AssetSourceViewSchema,\n TileViewSchema,\n])\n\n/** @public */\nexport type ViewDeclaration = z.output<typeof InterfaceDeclarationSchema>\n\nconst WebWorkerSchema = z.object({\n type: z.literal('worker'),\n ...interfaceDeclarationFields('Web worker'),\n})\n\n/** @public */\nexport type WebWorker = z.output<typeof WebWorkerSchema>\n\n/** @public */\nexport type DefineWebWorkerInput = Omit<WebWorker, 'type'>\n\n/** @public */\nexport function defineWebWorker(webWorker: DefineWebWorkerInput): WebWorker {\n return {...webWorker, type: 'worker'}\n}\n\n/** @internal */\nexport const ServiceDeclarationSchema = z.discriminatedUnion('type', [WebWorkerSchema])\n\nconst MediaLibraryFieldSchema = z.object({\n ...extensionDeclarationFields('Field'),\n public: z.optional(z.boolean()),\n title: z.string(),\n})\n\n/**\n * Stamped where the config crosses a boundary so the authoring model doesn't carry a constant discriminator.\n * @internal\n */\nexport const INSTALLATION_CONFIG_TYPE = 'installation_config'\n\n// `appType` is stamped by `unstable_defineMediaLibrary`, never authored.\nconst MediaLibraryConfigSchema = z.object({\n appType: z.literal('media-library'),\n fields: z\n .array(MediaLibraryFieldSchema)\n .check(\n z.refine(\n (fields) => new Set(fields.map((field) => field.name)).size === fields.length,\n 'Field `name` must be unique within a media library',\n ),\n ),\n})\n\n/**\n * A workbench config's built value, keyed by `appType`; deploys as a versioned\n * snapshot, not an interface.\n * @internal\n */\nexport const ConfigSchema = z.discriminatedUnion('appType', [MediaLibraryConfigSchema])\n\n/**\n * The `{appType, fields}` config value the build expands into a federation\n * remote and the deploy summarizes.\n * @internal\n */\nexport type WorkbenchConfigValue = z.output<typeof ConfigSchema>\n"],"names":["z","VIEW_CONTRACT_VERSION","SERVICE_CONTRACT_VERSION","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","VIEW_COMPONENTS","asset_source","panel","tile","window","TileSizeSchema","enum","DockGroupSchema","DockSchema","object","group","optional","order","number","ViewPlacementMetadataSchema","dock","TileInterfaceMetadataSchema","size","INTERFACE_CONTRACT_VERSIONS","undefined","worker","interfaceContractVersion","kind","version","String","interfaceModuleId","name","Error","extensionDeclarationFields","pattern","string","check","regex","src","interfaceDeclarationFields","title","PanelViewSchema","surface","literal","definePanelView","view","WindowViewSchema","defineWindowView","AssetSourceViewSchema","defineAssetSourceView","TileViewSchema","defineTileView","InterfaceDeclarationSchema","discriminatedUnion","WebWorkerSchema","type","defineWebWorker","webWorker","ServiceDeclarationSchema","MediaLibraryFieldSchema","public","boolean","INSTALLATION_CONFIG_TYPE","MediaLibraryConfigSchema","appType","fields","array","refine","Set","map","field","length","ConfigSchema"],"mappings":"AAAA,SAAQA,CAAC,QAAO,WAAU;AAE1B,4EAA4E;AAC5E,kEAAkE;AAClE,qCAAqC;AAErC,cAAc,GACd,OAAO,MAAMC,wBAAwB,EAAC;AAEtC,cAAc,GACd,OAAO,MAAMC,2BAA2B,EAAC;AAEzC,cAAc,GACd,OAAO,MAAMC,wCAAwC,EAAC;AActD,kGAAkG,GAClG,OAAO,MAAMC,kBAAkB;IAC7BC,cAAc;QAAC;KAAe;IAC9BC,OAAO;QAAC;QAAS;KAAQ;IACzBC,MAAM;QAAC;KAAO;IACdC,QAAQ,EAAE;AACZ,EAAsD;AAKtD;;;;;;CAMC,GACD,OAAO,MAAMC,iBAAiBT,EAAEU,IAAI,CAAC;IAAC;IAAS;IAAS;CAAS,EAAC;AAQlE,MAAMC,kBAAkBX,EAAEU,IAAI,CAAC;IAAC;IAAe;IAAqB;CAAY;AAKhF,cAAc,GACd,OAAO,MAAME,aAAaZ,EAAEa,MAAM,CAAC;IACjCC,OAAOd,EAAEe,QAAQ,CAACJ;IAClBK,OAAOhB,EAAEe,QAAQ,CAACf,EAAEiB,MAAM;AAC5B,GAAE;AAKF,cAAc,GACd,OAAO,MAAMC,8BAA8BlB,EAAEa,MAAM,CAAC;IAACM,MAAMP;AAAU,GAAE;AAKvE;;;;;CAKC,GACD,OAAO,MAAMQ,8BAA8BpB,EAAEa,MAAM,CAAC;IAClDG,OAAOhB,EAAEe,QAAQ,CAACf,EAAEiB,MAAM;IAC1BI,MAAMZ;AACR,GAAE;AAKF;;;;CAIC,GACD,MAAMa,8BAA8B;IAClCjB,cAAcJ;IACdK,OAAOL;IACPM,MAAMN;IACNO,QAAQe;IACRC,QAAQtB;AACV;AAKA,cAAc,GACd,OAAO,SAASuB,yBAAyBC,IAAmB;IAC1D,MAAMC,UAAUL,2BAA2B,CAACI,KAAK;IACjD,OAAOC,YAAYJ,YAAYA,YAAYK,OAAOD;AACpD;AAEA;;;;CAIC,GACD,OAAO,SAASE,kBAAkBH,IAAY,EAAEI,IAAY;IAC1D,OAAQJ;QACN,KAAK;QACL,KAAK;QACL,KAAK;YAAQ;gBACX,OAAO,CAAC,MAAM,EAAEI,MAAM;YACxB;QACA,KAAK;YAAU;gBACb,OAAO;YACT;QACA,KAAK;YAAU;gBACb,OAAO,CAAC,SAAS,EAAEA,MAAM;YAC3B;QACA;YAAS;gBACP,MAAM,IAAIC,MAAM,CAAC,qDAAqD,EAAEL,MAAM;YAChF;IACF;AACF;AAEA,qEAAqE;AACrE,SAASM,2BAA2BN,IAAqC;IACvE,MAAMO,UAAU;IAChB,OAAO;QACLH,MAAM9B,EAAEkC,MAAM,GAAGC,KAAK,CAACnC,EAAEoC,KAAK,CAACH,SAAS,GAAGP,KAAK,qBAAqB,EAAEO,SAAS;QAChFI,KAAKrC,EAAEkC,MAAM;IACf;AACF;AAEA,6DAA6D;AAC7D,mDAAmD;AACnD,SAASI,2BAA2BZ,IAA2B;IAC7D,OAAO;QACL,GAAGM,2BAA2BN,KAAK;QACnCa,OAAOvC,EAAEkC,MAAM,CAAC,GAAGR,KAAK,sBAAsB,CAAC;IACjD;AACF;AAEA,MAAMc,kBAAkBxC,EAAEa,MAAM,CAAC;IAC/B4B,SAASzC,EAAE0C,OAAO,CAAC;IACnB,GAAGJ,2BAA2B,OAAO;IACrCnB,MAAMnB,EAAEe,QAAQ,CAACH;AACnB;AAQA,YAAY,GACZ,OAAO,SAAS+B,gBAAgBC,IAA0B;IACxD,OAAO;QAAC,GAAGA,IAAI;QAAEH,SAAS;IAAO;AACnC;AAEA,MAAMI,mBAAmB7C,EAAEa,MAAM,CAAC;IAChC4B,SAASzC,EAAE0C,OAAO,CAAC;IACnB,GAAGJ,2BAA2B,OAAO;IACrCnB,MAAMnB,EAAEe,QAAQ,CAACH;AACnB;AAQA,YAAY,GACZ,OAAO,SAASkC,iBAAiBF,IAA2B;IAC1D,OAAO;QAAC,GAAGA,IAAI;QAAEH,SAAS;IAAQ;AACpC;AAEA,MAAMM,wBAAwB/C,EAAEa,MAAM,CAAC;IACrC4B,SAASzC,EAAE0C,OAAO,CAAC;IACnB,GAAGJ,2BAA2B,OAAO;AACvC;AAQA,YAAY,GACZ,OAAO,SAASU,sBAAsBJ,IAAgC;IACpE,OAAO;QAAC,GAAGA,IAAI;QAAEH,SAAS;IAAc;AAC1C;AAEA,MAAMQ,iBAAiBjD,EAAEa,MAAM,CAAC;IAC9B4B,SAASzC,EAAE0C,OAAO,CAAC;IACnB,GAAGJ,2BAA2B,OAAO;IACrC,gEAAgE,GAChEtB,OAAOhB,EAAEe,QAAQ,CAACf,EAAEiB,MAAM;IAC1B,yDAAyD,GACzDI,MAAMZ;AACR;AAQA,YAAY,GACZ,OAAO,SAASyC,eAAeN,IAAyB;IACtD,OAAO;QAAC,GAAGA,IAAI;QAAEH,SAAS;IAAM;AAClC;AAEA,cAAc,GACd,OAAO,MAAMU,6BAA6BnD,EAAEoD,kBAAkB,CAAC,WAAW;IACxEP;IACAL;IACAO;IACAE;CACD,EAAC;AAKF,MAAMI,kBAAkBrD,EAAEa,MAAM,CAAC;IAC/ByC,MAAMtD,EAAE0C,OAAO,CAAC;IAChB,GAAGJ,2BAA2B,aAAa;AAC7C;AAQA,YAAY,GACZ,OAAO,SAASiB,gBAAgBC,SAA+B;IAC7D,OAAO;QAAC,GAAGA,SAAS;QAAEF,MAAM;IAAQ;AACtC;AAEA,cAAc,GACd,OAAO,MAAMG,2BAA2BzD,EAAEoD,kBAAkB,CAAC,QAAQ;IAACC;CAAgB,EAAC;AAEvF,MAAMK,0BAA0B1D,EAAEa,MAAM,CAAC;IACvC,GAAGmB,2BAA2B,QAAQ;IACtC2B,QAAQ3D,EAAEe,QAAQ,CAACf,EAAE4D,OAAO;IAC5BrB,OAAOvC,EAAEkC,MAAM;AACjB;AAEA;;;CAGC,GACD,OAAO,MAAM2B,2BAA2B,sBAAqB;AAE7D,yEAAyE;AACzE,MAAMC,2BAA2B9D,EAAEa,MAAM,CAAC;IACxCkD,SAAS/D,EAAE0C,OAAO,CAAC;IACnBsB,QAAQhE,EACLiE,KAAK,CAACP,yBACNvB,KAAK,CACJnC,EAAEkE,MAAM,CACN,CAACF,SAAW,IAAIG,IAAIH,OAAOI,GAAG,CAAC,CAACC,QAAUA,MAAMvC,IAAI,GAAGT,IAAI,KAAK2C,OAAOM,MAAM,EAC7E;AAGR;AAEA;;;;CAIC,GACD,OAAO,MAAMC,eAAevE,EAAEoD,kBAAkB,CAAC,WAAW;IAACU;CAAyB,EAAC"}
|
package/dist/defineApp.js
CHANGED
|
@@ -35,6 +35,7 @@ import { DockSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } from
|
|
|
35
35
|
* on a studio is rejected (studio app views are not yet implemented).
|
|
36
36
|
*/ entry: z.optional(z.string("must be a path to the app's entry file")),
|
|
37
37
|
/** Optional icon override (path to an SVG). Wins over manifest/studio icon. */ icon: z.optional(z.string()),
|
|
38
|
+
/** Sanity-owned app deployed once and installed per org. @internal */ isSingleton: z.optional(z.boolean()),
|
|
38
39
|
/**
|
|
39
40
|
* Stable identity, distinct from the `slug` address. Defaults to `slug` when
|
|
40
41
|
* omitted. Identity keys — the build id and derived interface ids — are built
|
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 {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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"}
|
|
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 /** Sanity-owned app deployed once and installed per org. @internal */\n isSingleton: z.optional(z.boolean()),\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` and `isSingleton`.\n * @public\n */\nexport type DefineAppInput = Omit<\n z.output<typeof DefineAppInputSchema>,\n 'applicationType' | 'isSingleton'\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 `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","isSingleton","boolean","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.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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: '
|
|
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: 'window'\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 asset_source: AssetSourceViewComponents\n panel: PanelViewComponents\n tile: TileViewComponents\n window: ViewComponent<WindowViewProps>\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
|
@@ -3,7 +3,7 @@ import { interfaceContractVersion, interfaceModuleId } from './contract.js';
|
|
|
3
3
|
import { isWorkbenchApp } from './defineApp.js';
|
|
4
4
|
/** What a studio exposes as `./App` — the generated entry that renders `sanity.config.ts`. */ const GENERATED_ENTRY = `./${RUNTIME_DIR}/${FEDERATION_FILE_NAME}.jsx`;
|
|
5
5
|
/**
|
|
6
|
-
* `appTitle` titles the
|
|
6
|
+
* `appTitle` titles the window where a deploy resolved one through `--title`;
|
|
7
7
|
* dev takes it from the config.
|
|
8
8
|
* @internal
|
|
9
9
|
*/ export function deriveInterfaces(app, { appTitle, isApp }) {
|
|
@@ -14,7 +14,7 @@ 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
|
-
// Identity, not address: interface ids and the
|
|
17
|
+
// Identity, not address: interface ids and the window name key on `name`
|
|
18
18
|
// (which defaults to `slug`, so existing apps derive byte-identical ids).
|
|
19
19
|
const appName = app.name ?? app.slug;
|
|
20
20
|
const shared = (kind, declaration)=>({
|
|
@@ -54,7 +54,7 @@ import { isWorkbenchApp } from './defineApp.js';
|
|
|
54
54
|
surface: view.surface
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
if (view.surface === '
|
|
57
|
+
if (view.surface === 'panel' || view.surface === 'window') {
|
|
58
58
|
return {
|
|
59
59
|
...shared(view.surface, view),
|
|
60
60
|
metadata: placementMetadata(view.dock),
|
|
@@ -74,13 +74,13 @@ import { isWorkbenchApp } from './defineApp.js';
|
|
|
74
74
|
})),
|
|
75
75
|
...entry === undefined ? [] : [
|
|
76
76
|
{
|
|
77
|
-
...shared('
|
|
77
|
+
...shared('window', {
|
|
78
78
|
name: appName,
|
|
79
79
|
src: entry,
|
|
80
80
|
title: appTitle ?? app.title
|
|
81
81
|
}),
|
|
82
82
|
metadata: placementMetadata(),
|
|
83
|
-
surface: '
|
|
83
|
+
surface: 'window'
|
|
84
84
|
}
|
|
85
85
|
]
|
|
86
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 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: '
|
|
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: 'panel'})\n | (DerivedInterfaceBase & {metadata: ViewPlacementMetadata | null; surface: 'window'})\n\n/**\n * `appTitle` titles the window 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 window 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 === 'panel' || view.surface === 'window') {\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((webWorker): DerivedInterface => ({\n ...shared('worker', webWorker),\n metadata: null,\n type: 'worker',\n })),\n ...(entry === undefined\n ? []\n : [\n {\n ...shared('window', {name: appName, src: entry, title: appTitle ?? app.title}),\n metadata: placementMetadata(),\n surface: 'window' 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,yEAAyE;IACzE,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,WAAWD,KAAKC,OAAO,KAAK,UAAU;gBACzD,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,CAAC,CAACM,YAAiC,CAAA;gBAC9D,GAAGnB,OAAO,UAAUmB,UAAU;gBAC9BH,UAAU;gBACVI,MAAM;YACR,CAAA;WACI1B,UAAUC,YACV,EAAE,GACF;YACE;gBACE,GAAGK,OAAO,UAAU;oBAACF,MAAMD;oBAASQ,KAAKX;oBAAOY,OAAOd,YAAYD,IAAIe,KAAK;gBAAA,EAAE;gBAC9EU,UAAUR;gBACVO,SAAS;YACX;SACD;KACN;AACH"}
|
|
@@ -21,6 +21,9 @@ import { formatWorkbenchAppErrors, validateWorkbenchApp } from './validateWorkbe
|
|
|
21
21
|
applicationType: app.applicationType,
|
|
22
22
|
entry: app.entry,
|
|
23
23
|
icon: app.icon,
|
|
24
|
+
...app.isSingleton === undefined ? {} : {
|
|
25
|
+
isSingleton: app.isSingleton
|
|
26
|
+
},
|
|
24
27
|
// Identity defaults to the address, so existing apps behave identically.
|
|
25
28
|
name: app.name ?? app.slug,
|
|
26
29
|
organizationId: app.organizationId,
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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 /** Explicit singleton flag for Sanity-owned apps. */\n readonly isSingleton?: boolean\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 ...(app.isSingleton === undefined ? {} : {isSingleton: app.isSingleton}),\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","isSingleton","undefined","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;AA2CxF;;;;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,GAAIN,IAAIO,WAAW,KAAKC,YAAY,CAAC,IAAI;YAACD,aAAaP,IAAIO,WAAW;QAAA,CAAC;QACvE,yEAAyE;QACzEE,MAAMT,IAAIS,IAAI,IAAIT,IAAIU,IAAI;QAC1BC,gBAAgBX,IAAIW,cAAc;QAClCD,MAAMV,IAAIU,IAAI;QACdE,OAAOZ,IAAIY,KAAK,IAAI,EAAE;QACtBC,YAAYb,IAAIa,UAAU;QAC1BC,YAAYd,IAAIc,UAAU,IAAI,EAAE;IAClC;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workbench-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Internal implementation detail of the Sanity CLI's unstable workbench support. Not intended for direct use.",
|
|
5
5
|
"homepage": "https://github.com/sanity-io/cli",
|
|
6
6
|
"bugs": "https://github.com/sanity-io/cli/issues",
|
|
@@ -57,20 +57,20 @@
|
|
|
57
57
|
"access": "public"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@module-federation/runtime": "2.
|
|
61
|
-
"@module-federation/vite": "1.
|
|
62
|
-
"@sanity/types": "^6.
|
|
63
|
-
"@vitejs/plugin-react": "^6.1.
|
|
60
|
+
"@module-federation/runtime": "2.9.0",
|
|
61
|
+
"@module-federation/vite": "1.21.0",
|
|
62
|
+
"@sanity/types": "^6.11.0",
|
|
63
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
64
64
|
"form-data": "^4.0.5",
|
|
65
65
|
"rxjs": "^7.8.2",
|
|
66
66
|
"tar-fs": "^3.1.2",
|
|
67
|
-
"vite": "^8.2.
|
|
68
|
-
"zod": "^4.
|
|
69
|
-
"@sanity/cli-core": "^3.
|
|
67
|
+
"vite": "^8.2.2",
|
|
68
|
+
"zod": "^4.5.2",
|
|
69
|
+
"@sanity/cli-core": "^3.6.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@eslint/compat": "^2.1.0",
|
|
73
|
-
"@sanity/pkg-utils": "^12.
|
|
73
|
+
"@sanity/pkg-utils": "^12.3.4",
|
|
74
74
|
"@swc/cli": "^0.8.1",
|
|
75
75
|
"@swc/core": "^1.15.43",
|
|
76
76
|
"@types/node": "^22.20.0",
|