@sanity/workbench-cli 1.3.0 → 1.4.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.
Files changed (40) hide show
  1. package/dist/_exports/deploy.d.ts +13 -3
  2. package/dist/_exports/deploy.js +2 -1
  3. package/dist/_exports/deploy.js.map +1 -1
  4. package/dist/_exports/dev.d.ts +3 -2
  5. package/dist/_exports/index.d.ts +3 -2
  6. package/dist/_exports/undeploy.d.ts +248 -0
  7. package/dist/_exports/undeploy.js +3 -0
  8. package/dist/_exports/undeploy.js.map +1 -0
  9. package/dist/actions/build/vite/plugin.js +5 -0
  10. package/dist/actions/build/vite/plugin.js.map +1 -1
  11. package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js +5 -3
  12. package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js.map +1 -1
  13. package/dist/actions/build/vite/plugins/plugin-sanity-environment.js +26 -2
  14. package/dist/actions/build/vite/plugins/plugin-sanity-environment.js.map +1 -1
  15. package/dist/actions/deploy/buildExposes.js +5 -2
  16. package/dist/actions/deploy/buildExposes.js.map +1 -1
  17. package/dist/actions/deploy/checkBuiltOutput.js +4 -3
  18. package/dist/actions/deploy/checkBuiltOutput.js.map +1 -1
  19. package/dist/actions/deploy/deployConfig.js +11 -38
  20. package/dist/actions/deploy/deployConfig.js.map +1 -1
  21. package/dist/actions/deploy/deployWorkbenchApp.js +2 -79
  22. package/dist/actions/deploy/deployWorkbenchApp.js.map +1 -1
  23. package/dist/actions/deploy/getWorkbench.js.map +1 -1
  24. package/dist/actions/dev/deriveInterfaces.js +17 -20
  25. package/dist/actions/dev/deriveInterfaces.js.map +1 -1
  26. package/dist/actions/dev/exposesSetId.js +2 -2
  27. package/dist/actions/dev/exposesSetId.js.map +1 -1
  28. package/dist/actions/dev/registry.js +8 -7
  29. package/dist/actions/dev/registry.js.map +1 -1
  30. package/dist/actions/undeploy/workbenchUndeployAdapter.js +150 -0
  31. package/dist/actions/undeploy/workbenchUndeployAdapter.js.map +1 -0
  32. package/dist/defineView.js.map +1 -1
  33. package/dist/services/apiVersion.js.map +1 -0
  34. package/dist/services/applications.js +100 -0
  35. package/dist/services/applications.js.map +1 -0
  36. package/dist/services/installations.js +63 -0
  37. package/dist/services/installations.js.map +1 -0
  38. package/package.json +6 -2
  39. package/dist/actions/deploy/apiVersion.js.map +0 -1
  40. /package/dist/{actions/deploy → services}/apiVersion.js +0 -0
@@ -57,9 +57,10 @@ declare interface BuildExposesContext {
57
57
 
58
58
  /**
59
59
  * Throws unless `sourceDir` is a directory holding a federation build.
60
- * Workbench builds emit a module-federation remote instead of a static SPA,
61
- * so the usual `index.html` contract doesn't apply — `mf-manifest.json` is the
62
- * marker that `sanity build` produced a federation build.
60
+ * A workbench build always emits a module-federation remote, and may
61
+ * additionally emit a standalone `index.html` SPA (workbench remotes). Either
62
+ * way `mf-manifest.json` is the reliable marker that `sanity build` produced a
63
+ * federation build, so that — not `index.html` — is what we check for.
63
64
  */
64
65
  export declare function checkBuiltOutput(sourceDir: string): Promise<void>;
65
66
 
@@ -210,6 +211,7 @@ declare interface DeployableWorkbenchApp extends ResolvedWorkbenchApp {
210
211
  export declare function deployConfig(options: {
211
212
  appType: string;
212
213
  installationId: string;
214
+ organizationId: string;
213
215
  output: Output;
214
216
  sourceDir: string;
215
217
  version: string;
@@ -269,10 +271,17 @@ export declare function getApplication(
269
271
  applicationId: string,
270
272
  ): Promise<Application | null>;
271
273
 
274
+ /** Where a deployed application is served on its organization's workbench. */
275
+ export declare function getApplicationUrl(
276
+ application: Pick<Application, "id" | "organizationId" | "type">,
277
+ ): string;
278
+
272
279
  export declare function getWorkbench(
273
280
  cliConfig: CliConfig | null | undefined,
274
281
  ): DeployableWorkbenchApp | null;
275
282
 
283
+ export declare function getWorkbenchUrl(organizationId: string): string;
284
+
276
285
  /** @public */
277
286
  declare interface ResolvedWorkbenchApp {
278
287
  /** The app's unique `name` from `unstable_defineApp`. */
@@ -312,6 +321,7 @@ export declare function summarizeConfig(config: {
312
321
  appType: string;
313
322
  fields: {
314
323
  name: string;
324
+ src: string;
315
325
  title: string;
316
326
  }[];
317
327
  }): string;
@@ -1,7 +1,8 @@
1
1
  export { buildExposes, summarizeExposes } from '../actions/deploy/buildExposes.js';
2
2
  export { checkBuiltOutput } from '../actions/deploy/checkBuiltOutput.js';
3
3
  export { deployConfig, resolveInstallationId, summarizeConfig } from '../actions/deploy/deployConfig.js';
4
- export { deployCoreApp, deployStudio, getApplication } from '../actions/deploy/deployWorkbenchApp.js';
4
+ export { deployCoreApp, deployStudio } from '../actions/deploy/deployWorkbenchApp.js';
5
5
  export { getWorkbench } from '../actions/deploy/getWorkbench.js';
6
+ export { getApplication, getApplicationUrl, getWorkbenchUrl } from '../services/applications.js';
6
7
 
7
8
  //# sourceMappingURL=deploy.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/_exports/deploy.ts"],"sourcesContent":["export {\n type BrettInterface,\n buildExposes,\n type DeployedExpose,\n summarizeExposes,\n} from '../actions/deploy/buildExposes.js'\nexport {checkBuiltOutput} from '../actions/deploy/checkBuiltOutput.js'\nexport {\n deployConfig,\n resolveInstallationId,\n summarizeConfig,\n} from '../actions/deploy/deployConfig.js'\nexport {\n type Application,\n type BrettWorkspace,\n deployCoreApp,\n deployStudio,\n getApplication,\n} from '../actions/deploy/deployWorkbenchApp.js'\nexport {getWorkbench} from '../actions/deploy/getWorkbench.js'\n"],"names":["buildExposes","summarizeExposes","checkBuiltOutput","deployConfig","resolveInstallationId","summarizeConfig","deployCoreApp","deployStudio","getApplication","getWorkbench"],"mappings":"AAAA,SAEEA,YAAY,EAEZC,gBAAgB,QACX,oCAAmC;AAC1C,SAAQC,gBAAgB,QAAO,wCAAuC;AACtE,SACEC,YAAY,EACZC,qBAAqB,EACrBC,eAAe,QACV,oCAAmC;AAC1C,SAGEC,aAAa,EACbC,YAAY,EACZC,cAAc,QACT,0CAAyC;AAChD,SAAQC,YAAY,QAAO,oCAAmC"}
1
+ {"version":3,"sources":["../../src/_exports/deploy.ts"],"sourcesContent":["export {\n buildExposes,\n type DeployedExpose,\n summarizeExposes,\n} from '../actions/deploy/buildExposes.js'\nexport {checkBuiltOutput} from '../actions/deploy/checkBuiltOutput.js'\nexport {\n deployConfig,\n resolveInstallationId,\n summarizeConfig,\n} from '../actions/deploy/deployConfig.js'\nexport {deployCoreApp, deployStudio} from '../actions/deploy/deployWorkbenchApp.js'\nexport {getWorkbench} from '../actions/deploy/getWorkbench.js'\nexport {\n type Application,\n type BrettInterface,\n type BrettWorkspace,\n getApplication,\n getApplicationUrl,\n getWorkbenchUrl,\n} from '../services/applications.js'\n"],"names":["buildExposes","summarizeExposes","checkBuiltOutput","deployConfig","resolveInstallationId","summarizeConfig","deployCoreApp","deployStudio","getWorkbench","getApplication","getApplicationUrl","getWorkbenchUrl"],"mappings":"AAAA,SACEA,YAAY,EAEZC,gBAAgB,QACX,oCAAmC;AAC1C,SAAQC,gBAAgB,QAAO,wCAAuC;AACtE,SACEC,YAAY,EACZC,qBAAqB,EACrBC,eAAe,QACV,oCAAmC;AAC1C,SAAQC,aAAa,EAAEC,YAAY,QAAO,0CAAyC;AACnF,SAAQC,YAAY,QAAO,oCAAmC;AAC9D,SAIEC,cAAc,EACdC,iBAAiB,EACjBC,eAAe,QACV,8BAA6B"}
@@ -60,9 +60,10 @@ declare const devServerManifestSchema: z.ZodMiniObject<
60
60
  z.ZodMiniArray<
61
61
  z.ZodMiniObject<
62
62
  {
63
- entry_point: z.ZodMiniString<string>;
64
- interface_type: z.ZodMiniString<string>;
65
63
  name: z.ZodMiniString<string>;
64
+ src: z.ZodMiniString<string>;
65
+ title: z.ZodMiniString<string>;
66
+ type: z.ZodMiniString<string>;
66
67
  version: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
67
68
  },
68
69
  z.core.$strip
@@ -217,9 +217,10 @@ export declare interface PanelViewComponents {
217
217
  * @public
218
218
  */
219
219
  export declare type PanelViewProps = ViewComponentBaseProps<{
220
- entry_point: string;
221
- interface_type: "panel";
222
220
  name: string;
221
+ src: string;
222
+ title: string;
223
+ type: "panel";
223
224
  }>;
224
225
 
225
226
  /** @internal */
@@ -0,0 +1,248 @@
1
+ import { UndeployAdapter } from "@sanity/cli-core/undeploy";
2
+ import { UndeployApplicationTarget } from "@sanity/cli-core/undeploy";
3
+ import { UndeployConfigTarget } from "@sanity/cli-core/undeploy";
4
+ import { z } from "zod/mini";
5
+
6
+ /**
7
+ * The undeploy adapter for workbench apps, mirroring what a workbench deploy
8
+ * creates: apps that expose interfaces delete their Brett application (the
9
+ * server soft-deletes its deployments and refuses singletons with active
10
+ * installations); a singleton without interfaces — the media library — deletes
11
+ * its installation's config snapshots instead.
12
+ */
13
+ export declare function createWorkbenchUndeployAdapter(options: {
14
+ appId: string | undefined;
15
+ organizationId: string | undefined;
16
+ type: "coreApp" | "studio";
17
+ workbench: DeployableWorkbenchApp;
18
+ }): UndeployAdapter<WorkbenchUndeployTarget>;
19
+
20
+ /**
21
+ * User-facing input for `unstable_defineApp`. Excludes the internal
22
+ * `applicationType`, `isSingleton`, and `config` — validated by the
23
+ * schema but not part of the public surface (Sanity-owned apps set them via
24
+ * `@ts-expect-error`).
25
+ * @public
26
+ */
27
+ declare type DefineAppInput = Omit<
28
+ z.output<typeof DefineAppInputSchema>,
29
+ "applicationType" | "config" | "isSingleton"
30
+ >;
31
+
32
+ /**
33
+ * Runtime-validation schema for `unstable_defineApp`. Validates the full shape
34
+ * including the internal `applicationType`; the user-facing `DefineAppInput`
35
+ * type below omits that field.
36
+ * @internal
37
+ */
38
+ declare const DefineAppInputSchema: z.ZodMiniObject<
39
+ {
40
+ applicationType: z.ZodMiniOptional<
41
+ z.ZodMiniEnum<{
42
+ "media-library": "media-library";
43
+ coreApp: "coreApp";
44
+ studio: "studio";
45
+ canvas: "canvas";
46
+ dashboard: "dashboard";
47
+ }>
48
+ >;
49
+ config: z.ZodMiniOptional<
50
+ z.ZodMiniDiscriminatedUnion<
51
+ [
52
+ z.ZodMiniObject<
53
+ {
54
+ appType: z.ZodMiniLiteral<"media-library">;
55
+ fields: z.ZodMiniArray<
56
+ z.ZodMiniObject<
57
+ {
58
+ public: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
59
+ title: z.ZodMiniString<string>;
60
+ name: z.ZodMiniString<string>;
61
+ src: z.ZodMiniString<string>;
62
+ },
63
+ z.core.$strip
64
+ >
65
+ >;
66
+ },
67
+ z.core.$strip
68
+ >,
69
+ ],
70
+ "appType"
71
+ >
72
+ >;
73
+ entry: z.ZodMiniOptional<z.ZodMiniString<string>>;
74
+ group: z.ZodMiniOptional<
75
+ z.ZodMiniEnum<{
76
+ "dock.system": "dock.system";
77
+ "dock.applications": "dock.applications";
78
+ "dock.user": "dock.user";
79
+ }>
80
+ >;
81
+ icon: z.ZodMiniOptional<z.ZodMiniString<string>>;
82
+ isSingleton: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
83
+ name: z.ZodMiniString<string>;
84
+ organizationId: z.ZodMiniString<string>;
85
+ priority: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
86
+ services: z.ZodMiniOptional<
87
+ z.ZodMiniArray<
88
+ z.ZodMiniDiscriminatedUnion<
89
+ [
90
+ z.ZodMiniObject<
91
+ {
92
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
93
+ name: z.ZodMiniString<string>;
94
+ src: z.ZodMiniString<string>;
95
+ type: z.ZodMiniLiteral<"worker">;
96
+ },
97
+ z.core.$strip
98
+ >,
99
+ ],
100
+ "type"
101
+ >
102
+ >
103
+ >;
104
+ slug: z.ZodMiniOptional<z.ZodMiniString<string>>;
105
+ title: z.ZodMiniString<string>;
106
+ views: z.ZodMiniOptional<
107
+ z.ZodMiniArray<
108
+ z.ZodMiniDiscriminatedUnion<
109
+ [
110
+ z.ZodMiniObject<
111
+ {
112
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
113
+ name: z.ZodMiniString<string>;
114
+ src: z.ZodMiniString<string>;
115
+ type: z.ZodMiniLiteral<"panel">;
116
+ },
117
+ z.core.$strip
118
+ >,
119
+ ],
120
+ "type"
121
+ >
122
+ >
123
+ >;
124
+ },
125
+ z.core.$strip
126
+ >;
127
+
128
+ /**
129
+ * The branded result of `unstable_defineApp`. Carries the same fields as the
130
+ * input plus the internal brand — users only ever see `DefineAppInput`.
131
+ * @public
132
+ */
133
+ declare interface DefineAppResult extends DefineAppInput {
134
+ readonly [WORKBENCH_APP]: true;
135
+ }
136
+
137
+ declare interface DeployableWorkbenchApp extends ResolvedWorkbenchApp {
138
+ /**
139
+ * Throws when the app exposes nothing (no entry, view, service, or config) —
140
+ * the remote would have nothing to load. Gated before any prompt or API call.
141
+ */
142
+ assertDeployable(): void;
143
+ /**
144
+ * Validates the app's declared views into the application-service payload.
145
+ * Throws when a view declaration is malformed.
146
+ */
147
+ buildViewDeploymentPayload(applicationId: string): ViewDeploymentPayload;
148
+ /**
149
+ * A singleton (the Media Library) that carries an config — deploy
150
+ * persists the config to the org's installation. Independent of the interfaces,
151
+ * which register regardless; non-singletons never carry a config.
152
+ */
153
+ deploySingletonConfig: boolean;
154
+ /** Declares something to host as an application — an entry, view, or service. */
155
+ hasInterfaces: boolean;
156
+ }
157
+
158
+ /** A view or service as the deploy report and `--json` output surface it. */
159
+ declare interface DeployedExpose {
160
+ name: string;
161
+ src: string;
162
+ title: string;
163
+ type: string;
164
+ }
165
+
166
+ /** @public */
167
+ declare interface ResolvedWorkbenchApp {
168
+ /** The app's unique `name` from `unstable_defineApp`. */
169
+ readonly name: string;
170
+ /** Background worker services the app declares. */
171
+ readonly services: NonNullable<DefineAppInput["services"]>;
172
+ /** Dock panel views the app declares. */
173
+ readonly views: NonNullable<DefineAppInput["views"]>;
174
+ /** Resolved app kind — `studio` or one of the SDK app types. */
175
+ readonly applicationType?: string;
176
+ /** Deploys on its own path, separate from the interfaces. */
177
+ readonly config?: WorkbenchApp["config"];
178
+ /** SDK app-view entrypoint, when declared. */
179
+ readonly entry?: string;
180
+ /** Explicit singleton flag (a Sanity-owned app); `undefined` when the app doesn't set it. */
181
+ readonly isSingleton?: boolean;
182
+ /** Hostname the application is created at on first deploy. */
183
+ readonly slug?: string;
184
+ }
185
+
186
+ declare type ViewDeploymentPayload = z.infer<
187
+ typeof viewDeploymentPayloadSchema
188
+ >;
189
+
190
+ /**
191
+ * Payload registering an app's views with the application service on deploy.
192
+ *
193
+ * Phase 1 stub: the service that stores views does not exist yet, so the
194
+ * payload is validated and logged only — never sent. Builds the contract the
195
+ * application-service endpoint will accept.
196
+ */
197
+ declare const viewDeploymentPayloadSchema: z.ZodMiniObject<
198
+ {
199
+ applicationId: z.ZodMiniString<string>;
200
+ views: z.ZodMiniArray<
201
+ z.ZodMiniObject<
202
+ {
203
+ name: z.ZodMiniString<string>;
204
+ src: z.ZodMiniString<string>;
205
+ type: z.ZodMiniEnum<{
206
+ panel: "panel";
207
+ }>;
208
+ },
209
+ z.core.$loose
210
+ >
211
+ >;
212
+ },
213
+ z.core.$strip
214
+ >;
215
+
216
+ /**
217
+ * Nominal brand the CLI discriminates on to enable the workbench build/deploy
218
+ * codepath. Registered via `Symbol.for` so the marker survives module-realm
219
+ * boundaries — `@sanity/cli-core` re-derives the same global symbol with
220
+ * `Symbol.for` rather than importing it, so it stays internal to this module.
221
+ */
222
+ declare const WORKBENCH_APP: unique symbol;
223
+
224
+ /**
225
+ * A branded app as the CLI reads it — the full schema shape, including the
226
+ * internal fields `DefineAppInput` omits. Schema-derived so the narrowing
227
+ * can't drift from what the schema validates.
228
+ * @public
229
+ */
230
+ declare type WorkbenchApp = DefineAppResult &
231
+ z.output<typeof DefineAppInputSchema>;
232
+
233
+ /** The workbench extension of the shared target; serializes into `--json` as-is. */
234
+ declare type WorkbenchUndeployTarget =
235
+ | (UndeployApplicationTarget & {
236
+ /** Interfaces (views and services) registered by the application. */
237
+ interfaces: DeployedExpose[];
238
+ })
239
+ | (UndeployConfigTarget & {
240
+ /** The deployed config snapshots an undeploy deletes. */
241
+ configs: {
242
+ createdAt: string | null;
243
+ deployedBy: string | null;
244
+ id: string;
245
+ }[];
246
+ });
247
+
248
+ export {};
@@ -0,0 +1,3 @@
1
+ export { createWorkbenchUndeployAdapter } from '../actions/undeploy/workbenchUndeployAdapter.js';
2
+
3
+ //# sourceMappingURL=undeploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/_exports/undeploy.ts"],"sourcesContent":["export {createWorkbenchUndeployAdapter} from '../actions/undeploy/workbenchUndeployAdapter.js'\n"],"names":["createWorkbenchUndeployAdapter"],"mappings":"AAAA,SAAQA,8BAA8B,QAAO,kDAAiD"}
@@ -48,8 +48,13 @@ import { sanityFederationRuntime } from './plugins/plugin-sanity-federation-runt
48
48
  isApp: false,
49
49
  studioConfigPath: options.studioConfigPath
50
50
  };
51
+ // A workbench remote can also serve itself standalone. When the remote flag is
52
+ // set (and there's an `./App` to mount — never for a dock-only app), build the
53
+ // SPA client environment from the runtime bootstrap cli-build writes.
54
+ const clientInput = process.env.SANITY_INTERNAL_IS_WORKBENCH_REMOTE === 'true' && exposesApp ? path.join(workDir, '.sanity', 'runtime', 'app.js') : undefined;
51
55
  return [
52
56
  sanityEnvironmentPlugin({
57
+ clientInput,
53
58
  input: entryPath
54
59
  }),
55
60
  sanityFederationRuntime(runtimeOptions),
@@ -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 return [\n sanityEnvironmentPlugin({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","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,OAAO;QACL5B,wBAAwB;YAAC6B,OAAOX;QAAS;QACzChB,wBAAwByB;QACxB1B,yBAAyB;YAACkB;QAAS;QACnCpB,uBAAuB;YAACM,SAASqB;YAAmBpB;QAAI;KACzD;AACH,EAAC"}
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 // A workbench remote can also serve itself standalone. When the remote flag is\n // set (and there's an `./App` to mount — never for a dock-only app), build the\n // SPA client environment from the runtime bootstrap cli-build writes.\n const clientInput =\n process.env.SANITY_INTERNAL_IS_WORKBENCH_REMOTE === 'true' && exposesApp\n ? path.join(workDir, '.sanity', 'runtime', 'app.js')\n : 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","env","SANITY_INTERNAL_IS_WORKBENCH_REMOTE","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,+EAA+E;IAC/E,+EAA+E;IAC/E,sEAAsE;IACtE,MAAMC,cACJnB,QAAQoB,GAAG,CAACC,mCAAmC,KAAK,UAAUT,aAC1D5B,KAAKsC,IAAI,CAACvB,SAAS,WAAW,WAAW,YACzCgB;IAEN,OAAO;QACLzB,wBAAwB;YAAC6B;YAAaI,OAAOf;QAAS;QACtDhB,wBAAwByB;QACxB1B,yBAAyB;YAACkB;QAAS;QACnCpB,uBAAuB;YAACM,SAASqB;YAAmBpB;QAAI;KACzD;AACH,EAAC"}
@@ -2,7 +2,7 @@
2
2
  * Bake the app's bus identity into its bundle: `@sanity/runtime` reads
3
3
  * `__SANITY_APP_ID__` where it connects. `define` covers everything the
4
4
  * pipeline transforms (all of a production build, and dev-served source); the
5
- * esbuild define covers dev's pre-bundled dependencies, which skip Vite's
5
+ * rolldown define covers dev's pre-bundled dependencies, which skip Vite's
6
6
  * define transform.
7
7
  */ export function sanityAppId(appId) {
8
8
  const define = {
@@ -12,8 +12,10 @@
12
12
  config: ()=>({
13
13
  define,
14
14
  optimizeDeps: {
15
- esbuildOptions: {
16
- define
15
+ rolldownOptions: {
16
+ transform: {
17
+ define
18
+ }
17
19
  }
18
20
  }
19
21
  }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/actions/build/vite/plugins/plugin-sanity-app-id.ts"],"sourcesContent":["import {type Plugin} from 'vite'\n\n/**\n * Bake the app's bus identity into its bundle: `@sanity/runtime` reads\n * `__SANITY_APP_ID__` where it connects. `define` covers everything the\n * pipeline transforms (all of a production build, and dev-served source); the\n * esbuild define covers dev's pre-bundled dependencies, which skip Vite's\n * define transform.\n */\nexport function sanityAppId(appId: string): Plugin {\n const define = {__SANITY_APP_ID__: JSON.stringify(appId)}\n return {\n config: () => ({define, optimizeDeps: {esbuildOptions: {define}}}),\n name: 'sanity/workbench/app-id',\n }\n}\n"],"names":["sanityAppId","appId","define","__SANITY_APP_ID__","JSON","stringify","config","optimizeDeps","esbuildOptions","name"],"mappings":"AAEA;;;;;;CAMC,GACD,OAAO,SAASA,YAAYC,KAAa;IACvC,MAAMC,SAAS;QAACC,mBAAmBC,KAAKC,SAAS,CAACJ;IAAM;IACxD,OAAO;QACLK,QAAQ,IAAO,CAAA;gBAACJ;gBAAQK,cAAc;oBAACC,gBAAgB;wBAACN;oBAAM;gBAAC;YAAC,CAAA;QAChEO,MAAM;IACR;AACF"}
1
+ {"version":3,"sources":["../../../../../src/actions/build/vite/plugins/plugin-sanity-app-id.ts"],"sourcesContent":["import {type Plugin} from 'vite'\n\n/**\n * Bake the app's bus identity into its bundle: `@sanity/runtime` reads\n * `__SANITY_APP_ID__` where it connects. `define` covers everything the\n * pipeline transforms (all of a production build, and dev-served source); the\n * rolldown define covers dev's pre-bundled dependencies, which skip Vite's\n * define transform.\n */\nexport function sanityAppId(appId: string): Plugin {\n const define = {__SANITY_APP_ID__: JSON.stringify(appId)}\n return {\n config: () => ({define, optimizeDeps: {rolldownOptions: {transform: {define}}}}),\n name: 'sanity/workbench/app-id',\n }\n}\n"],"names":["sanityAppId","appId","define","__SANITY_APP_ID__","JSON","stringify","config","optimizeDeps","rolldownOptions","transform","name"],"mappings":"AAEA;;;;;;CAMC,GACD,OAAO,SAASA,YAAYC,KAAa;IACvC,MAAMC,SAAS;QAACC,mBAAmBC,KAAKC,SAAS,CAACJ;IAAM;IACxD,OAAO;QACLK,QAAQ,IAAO,CAAA;gBAACJ;gBAAQK,cAAc;oBAACC,iBAAiB;wBAACC,WAAW;4BAACP;wBAAM;oBAAC;gBAAC;YAAC,CAAA;QAC9EQ,MAAM;IACR;AACF"}
@@ -1,20 +1,44 @@
1
1
  import { FEDERATION_DIR_NAME } from '../constants.js';
2
2
  export function sanityEnvironmentPlugin(options) {
3
+ const { clientInput, input } = options;
3
4
  return {
4
5
  config () {
5
6
  return {
6
7
  builder: {
7
8
  async buildApp (builder) {
9
+ // `emptyOutDir` is false on both environments and the CLI clears
10
+ // `dist` once up-front, so the SPA and federation outputs coexist
11
+ // without either build wiping the other's files.
12
+ if (clientInput) {
13
+ await builder.build(builder.environments.client);
14
+ }
8
15
  await builder.build(builder.environments[FEDERATION_DIR_NAME]);
9
16
  }
10
17
  },
11
18
  environments: {
19
+ ...clientInput ? {
20
+ client: {
21
+ build: {
22
+ assetsDir: 'static',
23
+ copyPublicDir: false,
24
+ emptyOutDir: false,
25
+ outDir: `dist`,
26
+ rolldownOptions: {
27
+ input: {
28
+ sanity: clientInput
29
+ }
30
+ }
31
+ },
32
+ consumer: 'client'
33
+ }
34
+ } : {},
12
35
  [FEDERATION_DIR_NAME]: {
13
36
  build: {
14
37
  copyPublicDir: false,
38
+ emptyOutDir: false,
15
39
  outDir: `dist`,
16
- rollupOptions: {
17
- input: options.input
40
+ rolldownOptions: {
41
+ input
18
42
  }
19
43
  },
20
44
  consumer: 'client'
@@ -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\nexport function sanityEnvironmentPlugin(options: EnvironmentOptions): Plugin {\n return {\n config() {\n return {\n builder: {\n async buildApp(builder) {\n await builder.build(builder.environments[FEDERATION_DIR_NAME])\n },\n },\n environments: {\n [FEDERATION_DIR_NAME]: {\n build: {\n copyPublicDir: false,\n outDir: `dist`,\n rollupOptions: {input: options.input},\n },\n consumer: 'client',\n },\n },\n }\n },\n name: 'sanity/environment',\n }\n}\n"],"names":["FEDERATION_DIR_NAME","sanityEnvironmentPlugin","options","config","builder","buildApp","build","environments","copyPublicDir","outDir","rollupOptions","input","consumer","name"],"mappings":"AAEA,SAAQA,mBAAmB,QAAO,kBAAiB;AAMnD,OAAO,SAASC,wBAAwBC,OAA2B;IACjE,OAAO;QACLC;YACE,OAAO;gBACLC,SAAS;oBACP,MAAMC,UAASD,OAAO;wBACpB,MAAMA,QAAQE,KAAK,CAACF,QAAQG,YAAY,CAACP,oBAAoB;oBAC/D;gBACF;gBACAO,cAAc;oBACZ,CAACP,oBAAoB,EAAE;wBACrBM,OAAO;4BACLE,eAAe;4BACfC,QAAQ,CAAC,IAAI,CAAC;4BACdC,eAAe;gCAACC,OAAOT,QAAQS,KAAK;4BAAA;wBACtC;wBACAC,UAAU;oBACZ;gBACF;YACF;QACF;QACAC,MAAM;IACR;AACF"}
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 * Omitted for a dock-only app or when the workbench-remote SPA is disabled.\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 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;4BACLI,eAAe;4BACfC,aAAa;4BACbC,QAAQ,CAAC,IAAI,CAAC;4BACdC,iBAAiB;gCAACX;4BAAK;wBACzB;wBACAa,UAAU;oBACZ;gBACF;YACF;QACF;QACAC,MAAM;IACR;AACF"}
@@ -24,8 +24,11 @@
24
24
  for (const service of exposes.services ?? [])records.push(toRecord('services', service));
25
25
  return records;
26
26
  }
27
- function summarizeExposeGroup(heading, items) {
28
- const label = (item)=>item.title === item.name ? item.name : `${item.title} (${item.name})`;
27
+ const label = (item)=>item.title === item.name ? item.name : `${item.title} (${item.name})`;
28
+ /**
29
+ * One `Title (name): src` report line per declared entry point.
30
+ * @internal
31
+ */ export function summarizeExposeGroup(heading, items) {
29
32
  return `${heading}:\n${items.map((item)=>` ${label(item)}: ${item.src}`).join('\n')}`;
30
33
  }
31
34
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/buildExposes.ts"],"sourcesContent":["import {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\n\n/**\n * An interface as Brett stores it: the declared `type` (validated server-side,\n * not here) and the remote-relative `moduleId` the workbench loads it by — the\n * host prepends the app's own id.\n * @internal\n */\nexport interface BrettInterface {\n moduleId: string\n name: string\n title: string\n type: string\n version: string\n}\n\ninterface BuildExposesContext {\n appName: string\n appTitle: string\n /** Whether the build exposes the app view (`./App`) — apps with an `entry`, and every studio. */\n exposesAppView: boolean\n version: string\n}\n\n/**\n * The interface records deploy sends: the app view (only when `exposesAppView`),\n * every view, and every service.\n * @internal\n */\nexport function buildExposes(\n exposes: WorkbenchExposes,\n {appName, appTitle, exposesAppView, version}: BuildExposesContext,\n): BrettInterface[] {\n const toRecord = (\n prefix: string,\n decl: {name: string; title?: string; type: string},\n ): BrettInterface => ({\n moduleId: `${prefix}/${decl.name}`,\n name: decl.name,\n title: decl.title ?? decl.name,\n type: decl.type,\n version,\n })\n\n const records: BrettInterface[] = []\n if (exposesAppView) {\n records.push({moduleId: 'App', name: appName, title: appTitle, type: 'app', version})\n }\n for (const view of exposes.views ?? []) records.push(toRecord('views', view))\n for (const service of exposes.services ?? []) records.push(toRecord('services', service))\n return records\n}\n\n/** A view or service as the deploy report and `--json` output surface it. */\nexport interface DeployedExpose {\n name: string\n src: string\n title: string\n type: string\n}\n\nfunction summarizeExposeGroup(heading: string, items: readonly DeployedExpose[]): string {\n const label = (item: DeployedExpose) =>\n item.title === item.name ? item.name : `${item.title} (${item.name})`\n return `${heading}:\\n${items.map((item) => ` ${label(item)}: ${item.src}`).join('\\n')}`\n}\n\n/**\n * The deploy summary of an app's exposes: the structured records (for `--json`)\n * and one report line per non-empty group (for the human report).\n * @internal\n */\nexport function summarizeExposes({services, views}: WorkbenchExposes): {\n exposes: DeployedExpose[]\n lines: string[]\n} {\n const toExpose = (decl: {\n name: string\n src: string\n title?: string\n type: string\n }): DeployedExpose => ({\n name: decl.name,\n src: decl.src,\n title: decl.title ?? decl.name,\n type: decl.type,\n })\n const viewExposes = (views ?? []).map((view) => toExpose(view))\n const serviceExposes = (services ?? []).map((service) => toExpose(service))\n\n const lines: string[] = []\n if (viewExposes.length > 0) lines.push(summarizeExposeGroup('Views', viewExposes))\n if (serviceExposes.length > 0) lines.push(summarizeExposeGroup('Services', serviceExposes))\n return {exposes: [...viewExposes, ...serviceExposes], lines}\n}\n"],"names":["buildExposes","exposes","appName","appTitle","exposesAppView","version","toRecord","prefix","decl","moduleId","name","title","type","records","push","view","views","service","services","summarizeExposeGroup","heading","items","label","item","map","src","join","summarizeExposes","toExpose","viewExposes","serviceExposes","lines","length"],"mappings":"AAwBA;;;;CAIC,GACD,OAAO,SAASA,aACdC,OAAyB,EACzB,EAACC,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAEC,OAAO,EAAsB;IAEjE,MAAMC,WAAW,CACfC,QACAC,OACoB,CAAA;YACpBC,UAAU,GAAGF,OAAO,CAAC,EAAEC,KAAKE,IAAI,EAAE;YAClCA,MAAMF,KAAKE,IAAI;YACfC,OAAOH,KAAKG,KAAK,IAAIH,KAAKE,IAAI;YAC9BE,MAAMJ,KAAKI,IAAI;YACfP;QACF,CAAA;IAEA,MAAMQ,UAA4B,EAAE;IACpC,IAAIT,gBAAgB;QAClBS,QAAQC,IAAI,CAAC;YAACL,UAAU;YAAOC,MAAMR;YAASS,OAAOR;YAAUS,MAAM;YAAOP;QAAO;IACrF;IACA,KAAK,MAAMU,QAAQd,QAAQe,KAAK,IAAI,EAAE,CAAEH,QAAQC,IAAI,CAACR,SAAS,SAASS;IACvE,KAAK,MAAME,WAAWhB,QAAQiB,QAAQ,IAAI,EAAE,CAAEL,QAAQC,IAAI,CAACR,SAAS,YAAYW;IAChF,OAAOJ;AACT;AAUA,SAASM,qBAAqBC,OAAe,EAAEC,KAAgC;IAC7E,MAAMC,QAAQ,CAACC,OACbA,KAAKZ,KAAK,KAAKY,KAAKb,IAAI,GAAGa,KAAKb,IAAI,GAAG,GAAGa,KAAKZ,KAAK,CAAC,EAAE,EAAEY,KAAKb,IAAI,CAAC,CAAC,CAAC;IACvE,OAAO,GAAGU,QAAQ,GAAG,EAAEC,MAAMG,GAAG,CAAC,CAACD,OAAS,CAAC,EAAE,EAAED,MAAMC,MAAM,EAAE,EAAEA,KAAKE,GAAG,EAAE,EAAEC,IAAI,CAAC,OAAO;AAC1F;AAEA;;;;CAIC,GACD,OAAO,SAASC,iBAAiB,EAACT,QAAQ,EAAEF,KAAK,EAAmB;IAIlE,MAAMY,WAAW,CAACpB,OAKK,CAAA;YACrBE,MAAMF,KAAKE,IAAI;YACfe,KAAKjB,KAAKiB,GAAG;YACbd,OAAOH,KAAKG,KAAK,IAAIH,KAAKE,IAAI;YAC9BE,MAAMJ,KAAKI,IAAI;QACjB,CAAA;IACA,MAAMiB,cAAc,AAACb,CAAAA,SAAS,EAAE,AAAD,EAAGQ,GAAG,CAAC,CAACT,OAASa,SAASb;IACzD,MAAMe,iBAAiB,AAACZ,CAAAA,YAAY,EAAE,AAAD,EAAGM,GAAG,CAAC,CAACP,UAAYW,SAASX;IAElE,MAAMc,QAAkB,EAAE;IAC1B,IAAIF,YAAYG,MAAM,GAAG,GAAGD,MAAMjB,IAAI,CAACK,qBAAqB,SAASU;IACrE,IAAIC,eAAeE,MAAM,GAAG,GAAGD,MAAMjB,IAAI,CAACK,qBAAqB,YAAYW;IAC3E,OAAO;QAAC7B,SAAS;eAAI4B;eAAgBC;SAAe;QAAEC;IAAK;AAC7D"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/buildExposes.ts"],"sourcesContent":["import {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\nimport {type BrettInterface} from '../../services/applications.js'\n\ninterface BuildExposesContext {\n appName: string\n appTitle: string\n /** Whether the build exposes the app view (`./App`) — apps with an `entry`, and every studio. */\n exposesAppView: boolean\n version: string\n}\n\n/**\n * The interface records deploy sends: the app view (only when `exposesAppView`),\n * every view, and every service.\n * @internal\n */\nexport function buildExposes(\n exposes: WorkbenchExposes,\n {appName, appTitle, exposesAppView, version}: BuildExposesContext,\n): BrettInterface[] {\n const toRecord = (\n prefix: string,\n decl: {name: string; title?: string; type: string},\n ): BrettInterface => ({\n moduleId: `${prefix}/${decl.name}`,\n name: decl.name,\n title: decl.title ?? decl.name,\n type: decl.type,\n version,\n })\n\n const records: BrettInterface[] = []\n if (exposesAppView) {\n records.push({moduleId: 'App', name: appName, title: appTitle, type: 'app', version})\n }\n for (const view of exposes.views ?? []) records.push(toRecord('views', view))\n for (const service of exposes.services ?? []) records.push(toRecord('services', service))\n return records\n}\n\n/** A view or service as the deploy report and `--json` output surface it. */\nexport interface DeployedExpose {\n name: string\n src: string\n title: string\n type: string\n}\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 summarizeExposeGroup(\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 * The deploy summary of an app's exposes: the structured records (for `--json`)\n * and one report line per non-empty group (for the human report).\n * @internal\n */\nexport function summarizeExposes({services, views}: WorkbenchExposes): {\n exposes: DeployedExpose[]\n lines: string[]\n} {\n const toExpose = (decl: {\n name: string\n src: string\n title?: string\n type: string\n }): DeployedExpose => ({\n name: decl.name,\n src: decl.src,\n title: decl.title ?? decl.name,\n type: decl.type,\n })\n const viewExposes = (views ?? []).map((view) => toExpose(view))\n const serviceExposes = (services ?? []).map((service) => toExpose(service))\n\n const lines: string[] = []\n if (viewExposes.length > 0) lines.push(summarizeExposeGroup('Views', viewExposes))\n if (serviceExposes.length > 0) lines.push(summarizeExposeGroup('Services', serviceExposes))\n return {exposes: [...viewExposes, ...serviceExposes], lines}\n}\n"],"names":["buildExposes","exposes","appName","appTitle","exposesAppView","version","toRecord","prefix","decl","moduleId","name","title","type","records","push","view","views","service","services","label","item","summarizeExposeGroup","heading","items","map","src","join","summarizeExposes","toExpose","viewExposes","serviceExposes","lines","length"],"mappings":"AAWA;;;;CAIC,GACD,OAAO,SAASA,aACdC,OAAyB,EACzB,EAACC,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAEC,OAAO,EAAsB;IAEjE,MAAMC,WAAW,CACfC,QACAC,OACoB,CAAA;YACpBC,UAAU,GAAGF,OAAO,CAAC,EAAEC,KAAKE,IAAI,EAAE;YAClCA,MAAMF,KAAKE,IAAI;YACfC,OAAOH,KAAKG,KAAK,IAAIH,KAAKE,IAAI;YAC9BE,MAAMJ,KAAKI,IAAI;YACfP;QACF,CAAA;IAEA,MAAMQ,UAA4B,EAAE;IACpC,IAAIT,gBAAgB;QAClBS,QAAQC,IAAI,CAAC;YAACL,UAAU;YAAOC,MAAMR;YAASS,OAAOR;YAAUS,MAAM;YAAOP;QAAO;IACrF;IACA,KAAK,MAAMU,QAAQd,QAAQe,KAAK,IAAI,EAAE,CAAEH,QAAQC,IAAI,CAACR,SAAS,SAASS;IACvE,KAAK,MAAME,WAAWhB,QAAQiB,QAAQ,IAAI,EAAE,CAAEL,QAAQC,IAAI,CAACR,SAAS,YAAYW;IAChF,OAAOJ;AACT;AAUA,MAAMM,QAAQ,CAACC,OACbA,KAAKT,KAAK,KAAKS,KAAKV,IAAI,GAAGU,KAAKV,IAAI,GAAG,GAAGU,KAAKT,KAAK,CAAC,EAAE,EAAES,KAAKV,IAAI,CAAC,CAAC,CAAC;AAEvE;;;CAGC,GACD,OAAO,SAASW,qBACdC,OAAe,EACfC,KAA4D;IAE5D,OAAO,GAAGD,QAAQ,GAAG,EAAEC,MAAMC,GAAG,CAAC,CAACJ,OAAS,CAAC,EAAE,EAAED,MAAMC,MAAM,EAAE,EAAEA,KAAKK,GAAG,EAAE,EAAEC,IAAI,CAAC,OAAO;AAC1F;AAEA;;;;CAIC,GACD,OAAO,SAASC,iBAAiB,EAACT,QAAQ,EAAEF,KAAK,EAAmB;IAIlE,MAAMY,WAAW,CAACpB,OAKK,CAAA;YACrBE,MAAMF,KAAKE,IAAI;YACfe,KAAKjB,KAAKiB,GAAG;YACbd,OAAOH,KAAKG,KAAK,IAAIH,KAAKE,IAAI;YAC9BE,MAAMJ,KAAKI,IAAI;QACjB,CAAA;IACA,MAAMiB,cAAc,AAACb,CAAAA,SAAS,EAAE,AAAD,EAAGQ,GAAG,CAAC,CAACT,OAASa,SAASb;IACzD,MAAMe,iBAAiB,AAACZ,CAAAA,YAAY,EAAE,AAAD,EAAGM,GAAG,CAAC,CAACP,UAAYW,SAASX;IAElE,MAAMc,QAAkB,EAAE;IAC1B,IAAIF,YAAYG,MAAM,GAAG,GAAGD,MAAMjB,IAAI,CAACO,qBAAqB,SAASQ;IACrE,IAAIC,eAAeE,MAAM,GAAG,GAAGD,MAAMjB,IAAI,CAACO,qBAAqB,YAAYS;IAC3E,OAAO;QAAC7B,SAAS;eAAI4B;eAAgBC;SAAe;QAAEC;IAAK;AAC7D"}
@@ -2,9 +2,10 @@ import { stat } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  /**
4
4
  * Throws unless `sourceDir` is a directory holding a federation build.
5
- * Workbench builds emit a module-federation remote instead of a static SPA,
6
- * so the usual `index.html` contract doesn't apply — `mf-manifest.json` is the
7
- * marker that `sanity build` produced a federation build.
5
+ * A workbench build always emits a module-federation remote, and may
6
+ * additionally emit a standalone `index.html` SPA (workbench remotes). Either
7
+ * way `mf-manifest.json` is the reliable marker that `sanity build` produced a
8
+ * federation build, so that — not `index.html` — is what we check for.
8
9
  */ export async function checkBuiltOutput(sourceDir) {
9
10
  try {
10
11
  const stats = await stat(sourceDir);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/checkBuiltOutput.ts"],"sourcesContent":["import {stat} from 'node:fs/promises'\nimport {join} from 'node:path'\n\n/**\n * Throws unless `sourceDir` is a directory holding a federation build.\n * Workbench builds emit a module-federation remote instead of a static SPA,\n * so the usual `index.html` contract doesn't apply `mf-manifest.json` is the\n * marker that `sanity build` produced a federation build.\n */\nexport async function checkBuiltOutput(sourceDir: string): Promise<void> {\n try {\n const stats = await stat(sourceDir)\n if (!stats.isDirectory()) {\n throw new Error(`\"${sourceDir}\" is not a directory`)\n }\n } catch (err) {\n throw err.code === 'ENOENT' ? new Error(`Directory \"${sourceDir}\" does not exist`) : err\n }\n\n const manifestPath = join(sourceDir, 'mf-manifest.json')\n try {\n await stat(manifestPath)\n } catch (err) {\n throw err.code === 'ENOENT'\n ? new Error(\n `\"${manifestPath}\" does not exist. ` +\n 'The deploy directory must contain a federation build created with \"sanity build\".',\n )\n : err\n }\n}\n"],"names":["stat","join","checkBuiltOutput","sourceDir","stats","isDirectory","Error","err","code","manifestPath"],"mappings":"AAAA,SAAQA,IAAI,QAAO,mBAAkB;AACrC,SAAQC,IAAI,QAAO,YAAW;AAE9B;;;;;CAKC,GACD,OAAO,eAAeC,iBAAiBC,SAAiB;IACtD,IAAI;QACF,MAAMC,QAAQ,MAAMJ,KAAKG;QACzB,IAAI,CAACC,MAAMC,WAAW,IAAI;YACxB,MAAM,IAAIC,MAAM,CAAC,CAAC,EAAEH,UAAU,oBAAoB,CAAC;QACrD;IACF,EAAE,OAAOI,KAAK;QACZ,MAAMA,IAAIC,IAAI,KAAK,WAAW,IAAIF,MAAM,CAAC,WAAW,EAAEH,UAAU,gBAAgB,CAAC,IAAII;IACvF;IAEA,MAAME,eAAeR,KAAKE,WAAW;IACrC,IAAI;QACF,MAAMH,KAAKS;IACb,EAAE,OAAOF,KAAK;QACZ,MAAMA,IAAIC,IAAI,KAAK,WACf,IAAIF,MACF,CAAC,CAAC,EAAEG,aAAa,kBAAkB,CAAC,GAClC,uFAEJF;IACN;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/checkBuiltOutput.ts"],"sourcesContent":["import {stat} from 'node:fs/promises'\nimport {join} from 'node:path'\n\n/**\n * Throws unless `sourceDir` is a directory holding a federation build.\n * A workbench build always emits a module-federation remote, and may\n * additionally emit a standalone `index.html` SPA (workbench remotes). Either\n * way `mf-manifest.json` is the reliable marker that `sanity build` produced a\n * federation build, so that — not `index.html` — is what we check for.\n */\nexport async function checkBuiltOutput(sourceDir: string): Promise<void> {\n try {\n const stats = await stat(sourceDir)\n if (!stats.isDirectory()) {\n throw new Error(`\"${sourceDir}\" is not a directory`)\n }\n } catch (err) {\n throw err.code === 'ENOENT' ? new Error(`Directory \"${sourceDir}\" does not exist`) : err\n }\n\n const manifestPath = join(sourceDir, 'mf-manifest.json')\n try {\n await stat(manifestPath)\n } catch (err) {\n throw err.code === 'ENOENT'\n ? new Error(\n `\"${manifestPath}\" does not exist. ` +\n 'The deploy directory must contain a federation build created with \"sanity build\".',\n )\n : err\n }\n}\n"],"names":["stat","join","checkBuiltOutput","sourceDir","stats","isDirectory","Error","err","code","manifestPath"],"mappings":"AAAA,SAAQA,IAAI,QAAO,mBAAkB;AACrC,SAAQC,IAAI,QAAO,YAAW;AAE9B;;;;;;CAMC,GACD,OAAO,eAAeC,iBAAiBC,SAAiB;IACtD,IAAI;QACF,MAAMC,QAAQ,MAAMJ,KAAKG;QACzB,IAAI,CAACC,MAAMC,WAAW,IAAI;YACxB,MAAM,IAAIC,MAAM,CAAC,CAAC,EAAEH,UAAU,oBAAoB,CAAC;QACrD;IACF,EAAE,OAAOI,KAAK;QACZ,MAAMA,IAAIC,IAAI,KAAK,WAAW,IAAIF,MAAM,CAAC,WAAW,EAAEH,UAAU,gBAAgB,CAAC,IAAII;IACvF;IAEA,MAAME,eAAeR,KAAKE,WAAW;IACrC,IAAI;QACF,MAAMH,KAAKS;IACb,EAAE,OAAOF,KAAK;QACZ,MAAMA,IAAIC,IAAI,KAAK,WACf,IAAIF,MACF,CAAC,CAAC,EAAEG,aAAa,kBAAkB,CAAC,GAClC,uFAEJF;IACN;AACF"}