@sanity/workbench-cli 1.1.3 → 1.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.
Files changed (56) hide show
  1. package/dist/_exports/build.d.ts +81 -8
  2. package/dist/_exports/build.js.map +1 -1
  3. package/dist/_exports/deploy.d.ts +287 -12
  4. package/dist/_exports/deploy.js +4 -3
  5. package/dist/_exports/deploy.js.map +1 -1
  6. package/dist/_exports/dev.d.ts +25 -0
  7. package/dist/_exports/index.d.ts +85 -31
  8. package/dist/_exports/index.js +1 -11
  9. package/dist/_exports/index.js.map +1 -1
  10. package/dist/actions/build/artifact.js +8 -7
  11. package/dist/actions/build/artifact.js.map +1 -1
  12. package/dist/actions/build/configs/artifact.js +45 -0
  13. package/dist/actions/build/configs/artifact.js.map +1 -0
  14. package/dist/actions/build/vite/plugin.js +6 -14
  15. package/dist/actions/build/vite/plugin.js.map +1 -1
  16. package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js +24 -0
  17. package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js.map +1 -0
  18. package/dist/actions/build/vite/workbench-vite-plugins.js +8 -4
  19. package/dist/actions/build/vite/workbench-vite-plugins.js.map +1 -1
  20. package/dist/actions/deploy/apiVersion.js +5 -0
  21. package/dist/actions/deploy/apiVersion.js.map +1 -0
  22. package/dist/actions/deploy/buildExposes.js +56 -0
  23. package/dist/actions/deploy/buildExposes.js.map +1 -0
  24. package/dist/actions/deploy/checkBuiltOutput.js +25 -0
  25. package/dist/actions/deploy/checkBuiltOutput.js.map +1 -0
  26. package/dist/actions/deploy/deployConfig.js +90 -0
  27. package/dist/actions/deploy/deployConfig.js.map +1 -0
  28. package/dist/actions/deploy/deployWorkbenchApp.js +185 -0
  29. package/dist/actions/deploy/deployWorkbenchApp.js.map +1 -0
  30. package/dist/actions/deploy/getWorkbench.js +14 -25
  31. package/dist/actions/deploy/getWorkbench.js.map +1 -1
  32. package/dist/actions/deploy/viewDeployment.js +32 -0
  33. package/dist/actions/deploy/viewDeployment.js.map +1 -0
  34. package/dist/actions/dev/deriveInterfaces.js +63 -6
  35. package/dist/actions/dev/deriveInterfaces.js.map +1 -1
  36. package/dist/actions/dev/exposesSetId.js +69 -0
  37. package/dist/actions/dev/exposesSetId.js.map +1 -0
  38. package/dist/actions/dev/registry.js +27 -1
  39. package/dist/actions/dev/registry.js.map +1 -1
  40. package/dist/actions/dev/startDevManifestWatcher.js +2 -1
  41. package/dist/actions/dev/startDevManifestWatcher.js.map +1 -1
  42. package/dist/actions/dev/startDevServerRegistration.js +26 -11
  43. package/dist/actions/dev/startDevServerRegistration.js.map +1 -1
  44. package/dist/actions/dev/startWorkbenchDevServer.js +23 -5
  45. package/dist/actions/dev/startWorkbenchDevServer.js.map +1 -1
  46. package/dist/actions/dev/writeWorkbenchRuntime.js +4 -1
  47. package/dist/actions/dev/writeWorkbenchRuntime.js.map +1 -1
  48. package/dist/contract.js +43 -40
  49. package/dist/contract.js.map +1 -1
  50. package/dist/defineApp.js +60 -1
  51. package/dist/defineApp.js.map +1 -1
  52. package/dist/resolveWorkbenchApp.js +5 -1
  53. package/dist/resolveWorkbenchApp.js.map +1 -1
  54. package/package.json +7 -4
  55. package/dist/actions/dev/interfaceSetId.js +0 -51
  56. package/dist/actions/dev/interfaceSetId.js.map +0 -1
@@ -2,13 +2,14 @@ import { z } from "zod/mini";
2
2
 
3
3
  /**
4
4
  * User-facing input for `unstable_defineApp`. Excludes the internal
5
- * `applicationType` that field is validated by the schema but is not part of
6
- * the public surface (Sanity-owned apps set it via `@ts-expect-error`).
5
+ * `applicationType`, `isSingleton`, and `config` validated by the
6
+ * schema but not part of the public surface (Sanity-owned apps set them via
7
+ * `@ts-expect-error`).
7
8
  * @public
8
9
  */
9
10
  export declare type DefineAppInput = Omit<
10
11
  z.output<typeof DefineAppInputSchema>,
11
- "applicationType"
12
+ "applicationType" | "config" | "isSingleton"
12
13
  >;
13
14
 
14
15
  /**
@@ -21,13 +22,37 @@ declare const DefineAppInputSchema: z.ZodMiniObject<
21
22
  {
22
23
  applicationType: z.ZodMiniOptional<
23
24
  z.ZodMiniEnum<{
25
+ "media-library": "media-library";
24
26
  coreApp: "coreApp";
25
27
  studio: "studio";
26
28
  canvas: "canvas";
27
29
  dashboard: "dashboard";
28
- "media-library": "media-library";
29
30
  }>
30
31
  >;
32
+ config: z.ZodMiniOptional<
33
+ z.ZodMiniDiscriminatedUnion<
34
+ [
35
+ z.ZodMiniObject<
36
+ {
37
+ appType: z.ZodMiniLiteral<"media-library">;
38
+ fields: z.ZodMiniArray<
39
+ z.ZodMiniObject<
40
+ {
41
+ public: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
42
+ title: z.ZodMiniString<string>;
43
+ name: z.ZodMiniString<string>;
44
+ src: z.ZodMiniString<string>;
45
+ },
46
+ z.core.$strip
47
+ >
48
+ >;
49
+ },
50
+ z.core.$strip
51
+ >,
52
+ ],
53
+ "appType"
54
+ >
55
+ >;
31
56
  entry: z.ZodMiniOptional<z.ZodMiniString<string>>;
32
57
  group: z.ZodMiniOptional<
33
58
  z.ZodMiniEnum<{
@@ -37,6 +62,7 @@ declare const DefineAppInputSchema: z.ZodMiniObject<
37
62
  }>
38
63
  >;
39
64
  icon: z.ZodMiniOptional<z.ZodMiniString<string>>;
65
+ isSingleton: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
40
66
  name: z.ZodMiniString<string>;
41
67
  organizationId: z.ZodMiniString<string>;
42
68
  priority: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
@@ -46,6 +72,7 @@ declare const DefineAppInputSchema: z.ZodMiniObject<
46
72
  [
47
73
  z.ZodMiniObject<
48
74
  {
75
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
49
76
  name: z.ZodMiniString<string>;
50
77
  src: z.ZodMiniString<string>;
51
78
  type: z.ZodMiniLiteral<"worker">;
@@ -57,6 +84,7 @@ declare const DefineAppInputSchema: z.ZodMiniObject<
57
84
  >
58
85
  >
59
86
  >;
87
+ slug: z.ZodMiniOptional<z.ZodMiniString<string>>;
60
88
  title: z.ZodMiniString<string>;
61
89
  views: z.ZodMiniOptional<
62
90
  z.ZodMiniArray<
@@ -64,6 +92,7 @@ declare const DefineAppInputSchema: z.ZodMiniObject<
64
92
  [
65
93
  z.ZodMiniObject<
66
94
  {
95
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
67
96
  name: z.ZodMiniString<string>;
68
97
  src: z.ZodMiniString<string>;
69
98
  type: z.ZodMiniLiteral<"panel">;
@@ -116,6 +145,16 @@ export declare interface DefinedView<
116
145
  readonly version: typeof VIEW_CONTRACT_VERSION;
117
146
  }
118
147
 
148
+ /**
149
+ * Sanity-owned singleton, so authors don't name or title the app — only `organizationId` is required.
150
+ * @public
151
+ */
152
+ export declare interface DefineMediaLibraryInput {
153
+ /** Organization that owns the media library — the CLI runs and deploys against it. */
154
+ organizationId: string;
155
+ fields?: MediaLibraryField[];
156
+ }
157
+
119
158
  /**
120
159
  * Dock group identifier. The API does not block a user app from declaring a
121
160
  * reserved group (e.g. `dock.system`); priority conventions keep Sanity-owned
@@ -131,11 +170,28 @@ declare const DockGroupSchema: z.ZodMiniEnum<{
131
170
  "dock.user": "dock.user";
132
171
  }>;
133
172
 
173
+ /** @public */
174
+ export declare type InterfaceType = keyof typeof VIEW_COMPONENTS;
175
+
134
176
  /**
135
- * Every supported interface type the first argument to `unstable_defineView`.
177
+ * Whether `app` is a branded `unstable_defineApp(...)` result the sole
178
+ * workbench opt-in.
136
179
  * @public
137
180
  */
138
- export declare type InterfaceType = keyof typeof VIEW_COMPONENTS;
181
+ export declare function isWorkbenchApp(app: unknown): app is WorkbenchApp;
182
+
183
+ /**
184
+ * One custom field a media library exposes. `src` default-exports a `defineField(...)` schema type.
185
+ * @public
186
+ */
187
+ export declare interface MediaLibraryField {
188
+ /** Unique within the media library. */
189
+ name: string;
190
+ src: string;
191
+ title: string;
192
+ /** Readable outside the owning organization. */
193
+ public?: boolean;
194
+ }
139
195
 
140
196
  /**
141
197
  * A panel's view-component slot — the module-federation expose for one island.
@@ -166,12 +222,7 @@ export declare type PanelViewProps = ViewComponentBaseProps<{
166
222
  name: string;
167
223
  }>;
168
224
 
169
- /**
170
- * Contract version stamped on every defined service. Lets the workbench host
171
- * and the generated worker artifact evolve the service contract without
172
- * breaking already-deployed services; bumped only on a breaking change.
173
- * @internal
174
- */
225
+ /** @internal */
175
226
  declare const SERVICE_CONTRACT_VERSION = 1;
176
227
 
177
228
  /**
@@ -206,12 +257,7 @@ export declare interface ServiceInfo {
206
257
  readonly type: string;
207
258
  }
208
259
 
209
- /**
210
- * Every supported service type — the first argument to `unstable_defineService`.
211
- * Add a service type by adding its declaration schema below and registering it
212
- * here.
213
- * @public
214
- */
260
+ /** @public */
215
261
  export declare type ServiceType = "worker";
216
262
 
217
263
  /**
@@ -225,6 +271,14 @@ export declare function unstable_defineApp(
225
271
  input: DefineAppInput,
226
272
  ): DefineAppResult;
227
273
 
274
+ /**
275
+ * Declare the Sanity Media Library as a workbench app — a singleton whose `fields` become its config.
276
+ * @public
277
+ */
278
+ export declare function unstable_defineMediaLibrary(
279
+ input: DefineMediaLibraryInput,
280
+ ): DefineAppResult;
281
+
228
282
  /**
229
283
  * Define a Sanity Workbench background service. The first argument narrows the
230
284
  * callback shape — `"worker"` runs the callback inside a Web Worker, where it
@@ -256,20 +310,15 @@ export declare function unstable_defineView<TType extends InterfaceType>(
256
310
  ): DefinedView<TType>;
257
311
 
258
312
  /**
259
- * Component slots each interface type exposes, in render order the source of
260
- * truth for {@link InterfaceType} and for the build (the vite plugin expands a
261
- * view into one render artifact per component). Add a type by registering it here.
313
+ * Component slots each interface type exposes, in render order. Source of truth
314
+ * for {@link InterfaceType} and the build; add a type by registering it here.
262
315
  * @internal
263
316
  */
264
317
  declare const VIEW_COMPONENTS: {
265
318
  readonly panel: readonly ["title", "panel"];
266
319
  };
267
320
 
268
- /**
269
- * Contract version stamped on every defined view — lets the host and the
270
- * generated artifact evolve the contract without breaking deployed views.
271
- * @internal
272
- */
321
+ /** @internal */
273
322
  declare const VIEW_CONTRACT_VERSION = 1;
274
323
 
275
324
  /**
@@ -279,11 +328,7 @@ declare const VIEW_CONTRACT_VERSION = 1;
279
328
  */
280
329
  declare type ViewComponent<TProps> = (props: TProps) => unknown;
281
330
 
282
- /**
283
- * Props every view component receives, whatever its type. Per-type props
284
- * compose from this, so a prop added here reaches every view.
285
- * @public
286
- */
331
+ /** @public */
287
332
  declare interface ViewComponentBaseProps<TView> {
288
333
  view: TView;
289
334
  }
@@ -304,4 +349,13 @@ export declare interface ViewComponentsByType {
304
349
  */
305
350
  declare const WORKBENCH_APP: unique symbol;
306
351
 
352
+ /**
353
+ * A branded app as the CLI reads it — the full schema shape, including the
354
+ * internal fields `DefineAppInput` omits. Schema-derived so the narrowing
355
+ * can't drift from what the schema validates.
356
+ * @public
357
+ */
358
+ export declare type WorkbenchApp = DefineAppResult &
359
+ z.output<typeof DefineAppInputSchema>;
360
+
307
361
  export {};
@@ -1,14 +1,4 @@
1
- // Public, browser-safe entry for `@sanity/workbench-cli` — the authoring API app
2
- // authors call from `sanity.cli.ts` (re-exported by `sanity/cli` and the
3
- // `sanity` runtime entry). Calling `unstable_defineApp` is the *sole* workbench
4
- // opt-in: it stamps the global brand `Symbol.for('sanity.workbench.defineApp')`,
5
- // which the CLI discriminates on (see `isWorkbenchApp` in `@sanity/cli-core`).
6
- //
7
- // This module graph must stay browser-safe: zod-only, no `node:*`, no vite, no
8
- // `@sanity/cli-core`. View/service `src` files bundle to the browser, so anything
9
- // reachable from here ships in the frontend bundle. The Node-only build glue
10
- // lives behind the separate `./vite` entry and never leaks in.
11
- export { unstable_defineApp } from '../defineApp.js';
1
+ export { isWorkbenchApp, unstable_defineApp, unstable_defineMediaLibrary } from '../defineApp.js';
12
2
  export { unstable_defineService } from '../defineService.js';
13
3
  export { unstable_defineView } from '../defineView.js';
14
4
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/_exports/index.ts"],"sourcesContent":["// Public, browser-safe entry for `@sanity/workbench-cli` — the authoring API app\n// authors call from `sanity.cli.ts` (re-exported by `sanity/cli` and the\n// `sanity` runtime entry). Calling `unstable_defineApp` is the *sole* workbench\n// opt-in: it stamps the global brand `Symbol.for('sanity.workbench.defineApp')`,\n// which the CLI discriminates on (see `isWorkbenchApp` in `@sanity/cli-core`).\n//\n// This module graph must stay browser-safe: zod-only, no `node:*`, no vite, no\n// `@sanity/cli-core`. View/service `src` files bundle to the browser, so anything\n// reachable from here ships in the frontend bundle. The Node-only build glue\n// lives behind the separate `./vite` entry and never leaks in.\nexport type {InterfaceType, ServiceType} from '../contract.js'\nexport {unstable_defineApp} from '../defineApp.js'\nexport type {DefineAppInput, DefineAppResult, DockGroup} from '../defineApp.js'\nexport {unstable_defineService} from '../defineService.js'\nexport type {\n DefinedService,\n ServiceCallback,\n ServiceContext,\n ServiceInfo,\n} from '../defineService.js'\nexport {unstable_defineView} from '../defineView.js'\nexport type {\n DefinedView,\n PanelComponent,\n PanelViewComponents,\n PanelViewProps,\n ViewComponentsByType,\n} from '../defineView.js'\n"],"names":["unstable_defineApp","unstable_defineService","unstable_defineView"],"mappings":"AAAA,iFAAiF;AACjF,yEAAyE;AACzE,gFAAgF;AAChF,iFAAiF;AACjF,+EAA+E;AAC/E,EAAE;AACF,+EAA+E;AAC/E,kFAAkF;AAClF,6EAA6E;AAC7E,+DAA+D;AAE/D,SAAQA,kBAAkB,QAAO,kBAAiB;AAElD,SAAQC,sBAAsB,QAAO,sBAAqB;AAO1D,SAAQC,mBAAmB,QAAO,mBAAkB"}
1
+ {"version":3,"sources":["../../src/_exports/index.ts"],"sourcesContent":["export type {InterfaceType, ServiceType} from '../contract.js'\nexport {isWorkbenchApp, unstable_defineApp, unstable_defineMediaLibrary} from '../defineApp.js'\nexport type {\n DefineAppInput,\n DefineAppResult,\n DefineMediaLibraryInput,\n DockGroup,\n MediaLibraryField,\n WorkbenchApp,\n} from '../defineApp.js'\nexport {unstable_defineService} from '../defineService.js'\nexport type {\n DefinedService,\n ServiceCallback,\n ServiceContext,\n ServiceInfo,\n} from '../defineService.js'\nexport {unstable_defineView} from '../defineView.js'\nexport type {\n DefinedView,\n PanelComponent,\n PanelViewComponents,\n PanelViewProps,\n ViewComponentsByType,\n} from '../defineView.js'\n"],"names":["isWorkbenchApp","unstable_defineApp","unstable_defineMediaLibrary","unstable_defineService","unstable_defineView"],"mappings":"AACA,SAAQA,cAAc,EAAEC,kBAAkB,EAAEC,2BAA2B,QAAO,kBAAiB;AAS/F,SAAQC,sBAAsB,QAAO,sBAAqB;AAO1D,SAAQC,mBAAmB,QAAO,mBAAkB"}
@@ -1,3 +1,4 @@
1
+ import { configArtifacts } from './configs/artifact.js';
1
2
  import { serviceArtifacts } from './services/artifact.js';
2
3
  import { viewArtifacts } from './views/artifact.js';
3
4
  /**
@@ -15,14 +16,14 @@ import { viewArtifacts } from './views/artifact.js';
15
16
  return exposes;
16
17
  }
17
18
  /**
18
- * Expand a workbench app's declared views and services into the flat artifact
19
- * set the federation build writes and exposes — the single place that composes
20
- * the per-type expanders, so the expose mapping and the file writing read from
21
- * one expansion rather than re-deriving it.
22
- */ export function workbenchArtifacts(options) {
19
+ * Expand what the app exposes into the flat artifact set the federation build
20
+ * writes — the single place that composes the per-type expanders, so the expose
21
+ * mapping and the file writing read from one expansion.
22
+ */ export function workbenchArtifacts(exposes) {
23
23
  return [
24
- ...viewArtifacts(options.views),
25
- ...serviceArtifacts(options.services ?? [])
24
+ ...viewArtifacts(exposes.views ?? []),
25
+ ...serviceArtifacts(exposes.services ?? []),
26
+ ...configArtifacts(exposes.config)
26
27
  ];
27
28
  }
28
29
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/build/artifact.ts"],"sourcesContent":["import {type ServiceArtifact, serviceArtifacts} from './services/artifact.js'\nimport {type InterfaceArtifact, viewArtifacts} from './views/artifact.js'\n\n/**\n * What a {@link GeneratedArtifact.source} builder receives from the build — the\n * one thing it can't compute on its own, since only the build knows where the\n * runtime dir sits relative to the app's `src`.\n */\ninterface ArtifactContext {\n /** Import specifier for an app `src` file, relative to this artifact. */\n resolveImport: (src: string) => string\n}\n\n/**\n * One file the federation build generates into the runtime dir for a declared\n * interface. Each interface type — views, services — expands its declarations\n * into a flat list of these; the build then writes them and maps the ones the\n * host loads directly into the module-federation manifest.\n *\n * Adding an interface type is adding a builder that returns\n * `GeneratedArtifact[]` — the write loop and the expose mapping never change.\n */\nexport interface GeneratedArtifact {\n /** Path relative to the federation runtime dir, e.g. `views/feed/panel.js`. */\n path: string\n /** Build the file's contents. */\n source: (context: ArtifactContext) => string\n\n /**\n * Module-federation expose key when the host loads this artifact directly —\n * a view component `./views/feed/panel`, a service loader `./services/unread`.\n * Omitted for a file the host never loads on its own, like a worker bundle\n * (reached through its sibling loader).\n */\n expose?: string\n}\n\n/**\n * Map the artifacts the host loads directly (those with an `expose`) to their\n * runtime-dir paths, for the module-federation `exposes` field. `toExposePath`\n * turns a runtime-relative artifact path into the value federation wants — the\n * caller owns the runtime-dir location and any entry resolution.\n */\nexport function artifactExposes(\n artifacts: readonly GeneratedArtifact[],\n toExposePath: (artifactPath: string) => string,\n): Record<string, string> {\n const exposes: Record<string, string> = {}\n for (const artifact of artifacts) {\n if (artifact.expose) {\n exposes[artifact.expose] = toExposePath(artifact.path)\n }\n }\n return exposes\n}\n\n/**\n * Expand a workbench app's declared views and services into the flat artifact\n * set the federation build writes and exposes — the single place that composes\n * the per-type expanders, so the expose mapping and the file writing read from\n * one expansion rather than re-deriving it.\n */\nexport function workbenchArtifacts(options: {\n services?: readonly ServiceArtifact[]\n views: readonly InterfaceArtifact[]\n}): GeneratedArtifact[] {\n return [...viewArtifacts(options.views), ...serviceArtifacts(options.services ?? [])]\n}\n"],"names":["serviceArtifacts","viewArtifacts","artifactExposes","artifacts","toExposePath","exposes","artifact","expose","path","workbenchArtifacts","options","views","services"],"mappings":"AAAA,SAA8BA,gBAAgB,QAAO,yBAAwB;AAC7E,SAAgCC,aAAa,QAAO,sBAAqB;AAoCzE;;;;;CAKC,GACD,OAAO,SAASC,gBACdC,SAAuC,EACvCC,YAA8C;IAE9C,MAAMC,UAAkC,CAAC;IACzC,KAAK,MAAMC,YAAYH,UAAW;QAChC,IAAIG,SAASC,MAAM,EAAE;YACnBF,OAAO,CAACC,SAASC,MAAM,CAAC,GAAGH,aAAaE,SAASE,IAAI;QACvD;IACF;IACA,OAAOH;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASI,mBAAmBC,OAGlC;IACC,OAAO;WAAIT,cAAcS,QAAQC,KAAK;WAAMX,iBAAiBU,QAAQE,QAAQ,IAAI,EAAE;KAAE;AACvF"}
1
+ {"version":3,"sources":["../../../src/actions/build/artifact.ts"],"sourcesContent":["import {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\nimport {configArtifacts} from './configs/artifact.js'\nimport {serviceArtifacts} from './services/artifact.js'\nimport {viewArtifacts} from './views/artifact.js'\n\n/**\n * What a {@link GeneratedArtifact.source} builder receives from the build — the\n * one thing it can't compute on its own, since only the build knows where the\n * runtime dir sits relative to the app's `src`.\n */\ninterface ArtifactContext {\n /** Import specifier for an app `src` file, relative to this artifact. */\n resolveImport: (src: string) => string\n}\n\n/**\n * One file the federation build generates into the runtime dir for a declared\n * interface. Each interface type — views, services — expands its declarations\n * into a flat list of these; the build then writes them and maps the ones the\n * host loads directly into the module-federation manifest.\n *\n * Adding an interface type is adding a builder that returns\n * `GeneratedArtifact[]` — the write loop and the expose mapping never change.\n */\nexport interface GeneratedArtifact {\n /** Path relative to the federation runtime dir, e.g. `views/feed/panel.js`. */\n path: string\n /** Build the file's contents. */\n source: (context: ArtifactContext) => string\n\n /**\n * Module-federation expose key when the host loads this artifact directly —\n * a view component `./views/feed/panel`, a service loader `./services/unread`.\n * Omitted for a file the host never loads on its own, like a worker bundle\n * (reached through its sibling loader).\n */\n expose?: string\n}\n\n/**\n * Map the artifacts the host loads directly (those with an `expose`) to their\n * runtime-dir paths, for the module-federation `exposes` field. `toExposePath`\n * turns a runtime-relative artifact path into the value federation wants — the\n * caller owns the runtime-dir location and any entry resolution.\n */\nexport function artifactExposes(\n artifacts: readonly GeneratedArtifact[],\n toExposePath: (artifactPath: string) => string,\n): Record<string, string> {\n const exposes: Record<string, string> = {}\n for (const artifact of artifacts) {\n if (artifact.expose) {\n exposes[artifact.expose] = toExposePath(artifact.path)\n }\n }\n return exposes\n}\n\n/**\n * Expand what the app exposes into the flat artifact set the federation build\n * writes — the single place that composes the per-type expanders, so the expose\n * mapping and the file writing read from one expansion.\n */\nexport function workbenchArtifacts(exposes: WorkbenchExposes): GeneratedArtifact[] {\n return [\n ...viewArtifacts(exposes.views ?? []),\n ...serviceArtifacts(exposes.services ?? []),\n ...configArtifacts(exposes.config),\n ]\n}\n"],"names":["configArtifacts","serviceArtifacts","viewArtifacts","artifactExposes","artifacts","toExposePath","exposes","artifact","expose","path","workbenchArtifacts","views","services","config"],"mappings":"AACA,SAAQA,eAAe,QAAO,wBAAuB;AACrD,SAAQC,gBAAgB,QAAO,yBAAwB;AACvD,SAAQC,aAAa,QAAO,sBAAqB;AAoCjD;;;;;CAKC,GACD,OAAO,SAASC,gBACdC,SAAuC,EACvCC,YAA8C;IAE9C,MAAMC,UAAkC,CAAC;IACzC,KAAK,MAAMC,YAAYH,UAAW;QAChC,IAAIG,SAASC,MAAM,EAAE;YACnBF,OAAO,CAACC,SAASC,MAAM,CAAC,GAAGH,aAAaE,SAASE,IAAI;QACvD;IACF;IACA,OAAOH;AACT;AAEA;;;;CAIC,GACD,OAAO,SAASI,mBAAmBJ,OAAyB;IAC1D,OAAO;WACFJ,cAAcI,QAAQK,KAAK,IAAI,EAAE;WACjCV,iBAAiBK,QAAQM,QAAQ,IAAI,EAAE;WACvCZ,gBAAgBM,QAAQO,MAAM;KAClC;AACH"}
@@ -0,0 +1,45 @@
1
+ import { INSTALLATION_CONFIG_TYPE, MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION } from '../../../contract.js';
2
+ const CONFIGS_DIR_NAME = 'configs';
3
+ /** Expand the config into one federation module aggregating all fields, so the host imports one live config value. */ export function configArtifacts(config) {
4
+ if (!config) return [];
5
+ return [
6
+ {
7
+ expose: `./${CONFIGS_DIR_NAME}/${INSTALLATION_CONFIG_TYPE}`,
8
+ path: `${CONFIGS_DIR_NAME}/${INSTALLATION_CONFIG_TYPE}.js`,
9
+ source: ({ resolveImport })=>mediaLibraryConfigSource({
10
+ config,
11
+ resolveImport
12
+ })
13
+ }
14
+ ];
15
+ }
16
+ /**
17
+ * Emits `export const config`: each field's serializable `{name, title, public}`
18
+ * plus its live `defineField` value, the one thing the wire can't carry.
19
+ * `appType` stays off the module — the host assigns it from the wire record.
20
+ */ function mediaLibraryConfigSource(input) {
21
+ const { config, resolveImport } = input;
22
+ const imports = config.fields.map((field, index)=>`import field_${index} from ${JSON.stringify(resolveImport(field.src))}`).join('\n');
23
+ const entries = config.fields.map((field, index)=>` {name: ${JSON.stringify(field.name)}, title: ${JSON.stringify(field.title)}, ` + `public: ${JSON.stringify(Boolean(field.public))}, config: field_${index}},`).join('\n');
24
+ return `\
25
+ // This file is auto-generated on 'sanity build' / 'sanity dev'
26
+ // Modifications to this file are automatically discarded
27
+ ${imports}
28
+
29
+ export const type = ${JSON.stringify(INSTALLATION_CONFIG_TYPE)}
30
+ export const version = ${MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION}
31
+ export const config = {
32
+ fields: [
33
+ ${entries}
34
+ ],
35
+ }
36
+
37
+ if (import.meta.hot) {
38
+ // A config is data, not a rendered island with a live root to remount —
39
+ // self-accept so a field edit swaps the module in place; the host re-reads it.
40
+ import.meta.hot.accept()
41
+ }
42
+ `;
43
+ }
44
+
45
+ //# sourceMappingURL=artifact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/actions/build/configs/artifact.ts"],"sourcesContent":["import {INSTALLATION_CONFIG_TYPE, MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION} from '../../../contract.js'\nimport {type GeneratedArtifact} from '../artifact.js'\n\nconst CONFIGS_DIR_NAME = 'configs'\n\n/**\n * The config to generate a module for.\n * @internal\n */\nexport interface ConfigArtifact {\n fields: {name: string; public?: boolean; src: string; title: string}[]\n}\n\n/** Expand the config into one federation module aggregating all fields, so the host imports one live config value. */\nexport function configArtifacts(config: ConfigArtifact | undefined): GeneratedArtifact[] {\n if (!config) return []\n return [\n {\n expose: `./${CONFIGS_DIR_NAME}/${INSTALLATION_CONFIG_TYPE}`,\n path: `${CONFIGS_DIR_NAME}/${INSTALLATION_CONFIG_TYPE}.js`,\n source: ({resolveImport}) => mediaLibraryConfigSource({config, resolveImport}),\n },\n ]\n}\n\n/**\n * Emits `export const config`: each field's serializable `{name, title, public}`\n * plus its live `defineField` value, the one thing the wire can't carry.\n * `appType` stays off the module — the host assigns it from the wire record.\n */\nfunction mediaLibraryConfigSource(input: {\n config: ConfigArtifact\n resolveImport: (src: string) => string\n}): string {\n const {config, resolveImport} = input\n const imports = config.fields\n .map((field, index) => `import field_${index} from ${JSON.stringify(resolveImport(field.src))}`)\n .join('\\n')\n const entries = config.fields\n .map(\n (field, index) =>\n ` {name: ${JSON.stringify(field.name)}, title: ${JSON.stringify(field.title)}, ` +\n `public: ${JSON.stringify(Boolean(field.public))}, config: field_${index}},`,\n )\n .join('\\n')\n return `\\\n// This file is auto-generated on 'sanity build' / 'sanity dev'\n// Modifications to this file are automatically discarded\n${imports}\n\nexport const type = ${JSON.stringify(INSTALLATION_CONFIG_TYPE)}\nexport const version = ${MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION}\nexport const config = {\n fields: [\n${entries}\n ],\n}\n\nif (import.meta.hot) {\n // A config is data, not a rendered island with a live root to remount —\n // self-accept so a field edit swaps the module in place; the host re-reads it.\n import.meta.hot.accept()\n}\n`\n}\n"],"names":["INSTALLATION_CONFIG_TYPE","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","CONFIGS_DIR_NAME","configArtifacts","config","expose","path","source","resolveImport","mediaLibraryConfigSource","input","imports","fields","map","field","index","JSON","stringify","src","join","entries","name","title","Boolean","public"],"mappings":"AAAA,SAAQA,wBAAwB,EAAEC,qCAAqC,QAAO,uBAAsB;AAGpG,MAAMC,mBAAmB;AAUzB,oHAAoH,GACpH,OAAO,SAASC,gBAAgBC,MAAkC;IAChE,IAAI,CAACA,QAAQ,OAAO,EAAE;IACtB,OAAO;QACL;YACEC,QAAQ,CAAC,EAAE,EAAEH,iBAAiB,CAAC,EAAEF,0BAA0B;YAC3DM,MAAM,GAAGJ,iBAAiB,CAAC,EAAEF,yBAAyB,GAAG,CAAC;YAC1DO,QAAQ,CAAC,EAACC,aAAa,EAAC,GAAKC,yBAAyB;oBAACL;oBAAQI;gBAAa;QAC9E;KACD;AACH;AAEA;;;;CAIC,GACD,SAASC,yBAAyBC,KAGjC;IACC,MAAM,EAACN,MAAM,EAAEI,aAAa,EAAC,GAAGE;IAChC,MAAMC,UAAUP,OAAOQ,MAAM,CAC1BC,GAAG,CAAC,CAACC,OAAOC,QAAU,CAAC,aAAa,EAAEA,MAAM,MAAM,EAAEC,KAAKC,SAAS,CAACT,cAAcM,MAAMI,GAAG,IAAI,EAC9FC,IAAI,CAAC;IACR,MAAMC,UAAUhB,OAAOQ,MAAM,CAC1BC,GAAG,CACF,CAACC,OAAOC,QACN,CAAC,WAAW,EAAEC,KAAKC,SAAS,CAACH,MAAMO,IAAI,EAAE,SAAS,EAAEL,KAAKC,SAAS,CAACH,MAAMQ,KAAK,EAAE,EAAE,CAAC,GACnF,CAAC,QAAQ,EAAEN,KAAKC,SAAS,CAACM,QAAQT,MAAMU,MAAM,GAAG,gBAAgB,EAAET,MAAM,EAAE,CAAC,EAE/EI,IAAI,CAAC;IACR,OAAO,CAAC;;;AAGV,EAAER,QAAQ;;oBAEU,EAAEK,KAAKC,SAAS,CAACjB,0BAA0B;uBACxC,EAAEC,sCAAsC;;;AAG/D,EAAEmB,QAAQ;;;;;;;;;AASV,CAAC;AACD"}
@@ -8,7 +8,7 @@ import { sanityFederationRuntime } from './plugins/plugin-sanity-federation-runt
8
8
  /**
9
9
  * @internal
10
10
  */ export const federation = (options)=>{
11
- const { exposes: defaultExposes = {}, name: defaultName, pkgJson, services = [], views = [], workDir = process.cwd() } = options;
11
+ const { exposes, name: defaultName, pkgJson, workDir = process.cwd() } = options;
12
12
  let name = defaultName;
13
13
  if (!name) {
14
14
  name = pkgJson?.name;
@@ -25,29 +25,21 @@ import { sanityFederationRuntime } from './plugins/plugin-sanity-federation-runt
25
25
  return resolvedPath;
26
26
  }
27
27
  const entryPath = resolveEntryPath(generatedEntry);
28
- const resolvedExposes = {};
29
- for (const [key, exposePath] of Object.entries(defaultExposes)){
30
- resolvedExposes[key] = resolveEntryPath(exposePath) ?? exposePath;
31
- }
32
28
  // Each view component (`./views/<view>/<component>`) and each service loader
33
29
  // (`./services/<name>`) is exposed straight to the host, pointing at the file
34
30
  // the extension-artifacts plugin generates under RUNTIME_DIR. A service's
35
31
  // worker bundle carries no expose — the host reaches it through its loader.
36
- const artifacts = workbenchArtifacts({
37
- services,
38
- views
39
- });
40
- const interfaceExposes = artifactExposes(artifacts, (artifactPath)=>resolveEntryPath(`./${RUNTIME_DIR}/${artifactPath}`));
32
+ const artifacts = workbenchArtifacts(exposes ?? {});
33
+ const artifactModuleExposes = artifactExposes(artifacts, (artifactPath)=>resolveEntryPath(`./${RUNTIME_DIR}/${artifactPath}`));
41
34
  // A dock-only app (`isApp` with no `appEntry`) has no navigable full-page
42
35
  // view, so it exposes no `./App` — only its views. Studios and apps with an
43
36
  // entry expose `./App` (the generated render entry).
44
37
  const exposesApp = !options.isApp || options.appEntry !== undefined;
45
- const exposes = {
38
+ const federationExposes = {
46
39
  ...exposesApp ? {
47
40
  './App': entryPath
48
41
  } : {},
49
- ...resolvedExposes,
50
- ...interfaceExposes
42
+ ...artifactModuleExposes
51
43
  };
52
44
  const runtimeOptions = options.isApp ? {
53
45
  appEntry: options.appEntry,
@@ -65,7 +57,7 @@ import { sanityFederationRuntime } from './plugins/plugin-sanity-federation-runt
65
57
  artifacts
66
58
  }),
67
59
  sanityModuleFederation({
68
- exposes,
60
+ exposes: federationExposes,
69
61
  name
70
62
  })
71
63
  ];
@@ -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 DefineAppInput} from '../../../defineApp.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?: Record<string, string>\n pkgJson?: PackageJson\n /**\n * Background services the app declares. Each is built into a self-contained\n * worker bundle plus a loader module exposed as `./services/<name>`, so the\n * workbench host can fetch the worker URL and run it.\n */\n services?: DefineAppInput['services']\n /**\n * Views the app declares. Each component of a view is built into a\n * render-contract artifact and exposed as `./views/<view>/<component>` so the\n * workbench host can load each as its own island.\n */\n views?: DefineAppInput['views']\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 {\n exposes: defaultExposes = {},\n name: defaultName,\n pkgJson,\n services = [],\n views = [],\n workDir = process.cwd(),\n } = 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 const resolvedExposes: Record<string, string> = {}\n for (const [key, exposePath] of Object.entries(defaultExposes)) {\n resolvedExposes[key] = resolveEntryPath(exposePath) ?? exposePath\n }\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({services, views})\n const interfaceExposes = 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 exposes: NonNullable<ModuleFederationOptions['exposes']> = {\n ...(exposesApp ? {'./App': entryPath} : {}),\n ...resolvedExposes,\n ...interfaceExposes,\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, name}),\n ]\n}\n"],"names":["path","artifactExposes","workbenchArtifacts","FEDERATION_FILE_NAME","RUNTIME_DIR","sanityModuleFederation","sanityEnvironmentPlugin","sanityExtensionArtifacts","sanityFederationRuntime","federation","options","exposes","defaultExposes","name","defaultName","pkgJson","services","views","workDir","process","cwd","Error","generatedEntry","resolveEntryPath","entry","resolvedPath","resolve","entryPath","resolvedExposes","key","exposePath","Object","entries","artifacts","interfaceExposes","artifactPath","exposesApp","isApp","appEntry","undefined","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;AAuDtD;;CAEC,GACD,OAAO,MAAMC,aAAa,CAACC;IACzB,MAAM,EACJC,SAASC,iBAAiB,CAAC,CAAC,EAC5BC,MAAMC,WAAW,EACjBC,OAAO,EACPC,WAAW,EAAE,EACbC,QAAQ,EAAE,EACVC,UAAUC,QAAQC,GAAG,EAAE,EACxB,GAAGV;IAEJ,IAAIG,OAAOC;IAEX,IAAI,CAACD,MAAM;QACTA,OAAOE,SAASF;IAClB;IAEA,IAAI,CAACA,MAAM;QACT,MAAM,IAAIQ,MAAM;IAClB;IAEA,MAAMC,iBAAiB,CAAC,EAAE,EAAElB,YAAY,CAAC,EAAED,qBAAqB,IAAI,CAAC;IAErE,SAASoB,iBAAiBC,KAAa;QACrC,MAAMC,eAAezB,KAAK0B,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,MAAMM,kBAA0C,CAAC;IACjD,KAAK,MAAM,CAACC,KAAKC,WAAW,IAAIC,OAAOC,OAAO,CAACpB,gBAAiB;QAC9DgB,eAAe,CAACC,IAAI,GAAGN,iBAAiBO,eAAeA;IACzD;IAEA,6EAA6E;IAC7E,8EAA8E;IAC9E,0EAA0E;IAC1E,4EAA4E;IAC5E,MAAMG,YAAY/B,mBAAmB;QAACc;QAAUC;IAAK;IACrD,MAAMiB,mBAAmBjC,gBAAgBgC,WAAW,CAACE,eACnDZ,iBAAiB,CAAC,EAAE,EAAEnB,YAAY,CAAC,EAAE+B,cAAc;IAGrD,0EAA0E;IAC1E,4EAA4E;IAC5E,qDAAqD;IACrD,MAAMC,aAAa,CAAC1B,QAAQ2B,KAAK,IAAI3B,QAAQ4B,QAAQ,KAAKC;IAE1D,MAAM5B,UAA2D;QAC/D,GAAIyB,aAAa;YAAC,SAAST;QAAS,IAAI,CAAC,CAAC;QAC1C,GAAGC,eAAe;QAClB,GAAGM,gBAAgB;IACrB;IAEA,MAAMM,iBAA2C9B,QAAQ2B,KAAK,GAC1D;QAACC,UAAU5B,QAAQ4B,QAAQ;QAAED,OAAO;IAAI,IACxC;QAACA,OAAO;QAAOI,kBAAkB/B,QAAQ+B,gBAAgB;IAAA;IAE7D,OAAO;QACLnC,wBAAwB;YAACoC,OAAOf;QAAS;QACzCnB,wBAAwBgC;QACxBjC,yBAAyB;YAAC0B;QAAS;QACnC5B,uBAAuB;YAACM;YAASE;QAAI;KACtC;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 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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Bake the app's bus identity into its bundle: `@sanity/runtime` reads
3
+ * `__SANITY_APP_ID__` where it connects. `define` covers everything the
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
6
+ * define transform.
7
+ */ export function sanityAppId(appId) {
8
+ const define = {
9
+ __SANITY_APP_ID__: JSON.stringify(appId)
10
+ };
11
+ return {
12
+ config: ()=>({
13
+ define,
14
+ optimizeDeps: {
15
+ esbuildOptions: {
16
+ define
17
+ }
18
+ }
19
+ }),
20
+ name: 'sanity/workbench/app-id'
21
+ };
22
+ }
23
+
24
+ //# sourceMappingURL=plugin-sanity-app-id.js.map
@@ -0,0 +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"}
@@ -7,6 +7,7 @@
7
7
  import path from 'node:path';
8
8
  import { readPackageJson } from '@sanity/cli-core';
9
9
  import { federation } from './plugin.js';
10
+ import { sanityAppId } from './plugins/plugin-sanity-app-id.js';
10
11
  /**
11
12
  * A workbench studio renders from its `sanity.config.*`, so the build needs one.
12
13
  * An explicit `applicationType: 'studio'` wins over detection, so a studio can
@@ -19,9 +20,9 @@ import { federation } from './plugin.js';
19
20
  return relativeConfigLocation;
20
21
  }
21
22
  /** Build the Vite plugins for a workbench app's module-federation remote. */ export async function workbenchVitePlugins(options) {
22
- const { cwd, entries, isApp, services, views } = options;
23
+ const { appId, cwd, entries, exposes, isApp } = options;
23
24
  const pkgJson = await readPackageJson(path.join(cwd, 'package.json'));
24
- return federation({
25
+ const federationPlugin = federation({
25
26
  ...isApp ? {
26
27
  // `null` relativeEntry (a branded app with no `entry`) → omit `appEntry`,
27
28
  // so the remote exposes no `./App`, only its views.
@@ -33,11 +34,14 @@ import { federation } from './plugin.js';
33
34
  isApp: false,
34
35
  studioConfigPath: requireStudioConfigPath(entries.relativeConfigLocation)
35
36
  },
37
+ exposes,
36
38
  pkgJson,
37
- services,
38
- views,
39
39
  workDir: cwd
40
40
  });
41
+ return appId === undefined ? federationPlugin : [
42
+ federationPlugin,
43
+ sanityAppId(appId)
44
+ ];
41
45
  }
42
46
 
43
47
  //# sourceMappingURL=workbench-vite-plugins.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/build/vite/workbench-vite-plugins.ts"],"sourcesContent":["// The build-facing entry of the workbench federation stack: turn a workbench\n// app's build inputs into the Vite plugins that produce its module-federation\n// remote. `@sanity/cli-build`'s `getViteConfig` calls this instead of\n// assembling `federation`'s options itself, so the discriminated app-vs-studio\n// option shape, the no-app-view rule, and the studio-config requirement all\n// live here next to `federation` — the build package just hands over its inputs.\n\nimport path from 'node:path'\n\nimport {readPackageJson} from '@sanity/cli-core'\nimport {type PluginOption} from 'vite'\n\nimport {type DefineAppInput} from '../../../defineApp.js'\nimport {federation} from './plugin.js'\n\ninterface WorkbenchViteOptions {\n /** Project root — read for the federation remote name, and the plugin workDir. */\n cwd: string\n /**\n * Build entry paths relative to the federation runtime dir. `relativeEntry` is\n * the app's `entry` (null for a dock-only app with no app view);\n * `relativeConfigLocation` is the studio's `sanity.config.*` (null when absent).\n */\n entries: {relativeConfigLocation: string | null; relativeEntry: string | null}\n\n /** App (vs studio) build selects the discriminated federation option shape. */\n isApp?: boolean\n /** Declared background services. */\n services?: DefineAppInput['services']\n /** Declared dock views. */\n views?: DefineAppInput['views']\n}\n\n/**\n * A workbench studio renders from its `sanity.config.*`, so the build needs one.\n * An explicit `applicationType: 'studio'` wins over detection, so a studio can\n * reach here with no config file — fail with the fix rather than a cryptic build\n * error downstream.\n */\nfunction requireStudioConfigPath(relativeConfigLocation: string | null): string {\n if (relativeConfigLocation === null) {\n throw new Error(\n 'Workbench studios need a sanity.config.js or sanity.config.ts file. ' +\n \"Add one, or remove `applicationType: 'studio'` from `unstable_defineApp` \" +\n 'to let the CLI infer the application type.',\n )\n }\n return relativeConfigLocation\n}\n\n/** Build the Vite plugins for a workbench app's module-federation remote. */\nexport async function workbenchVitePlugins(options: WorkbenchViteOptions): Promise<PluginOption> {\n const {cwd, entries, isApp, services, views} = options\n const pkgJson = await readPackageJson(path.join(cwd, 'package.json'))\n\n return federation({\n ...(isApp\n ? {\n // `null` relativeEntry (a branded app with no `entry`) → omit `appEntry`,\n // so the remote exposes no `./App`, only its views.\n ...(entries.relativeEntry ? {appEntry: entries.relativeEntry} : {}),\n isApp: true as const,\n }\n : {\n isApp: false as const,\n studioConfigPath: requireStudioConfigPath(entries.relativeConfigLocation),\n }),\n pkgJson,\n services,\n views,\n workDir: cwd,\n })\n}\n"],"names":["path","readPackageJson","federation","requireStudioConfigPath","relativeConfigLocation","Error","workbenchVitePlugins","options","cwd","entries","isApp","services","views","pkgJson","join","relativeEntry","appEntry","studioConfigPath","workDir"],"mappings":"AAAA,6EAA6E;AAC7E,8EAA8E;AAC9E,sEAAsE;AACtE,+EAA+E;AAC/E,4EAA4E;AAC5E,iFAAiF;AAEjF,OAAOA,UAAU,YAAW;AAE5B,SAAQC,eAAe,QAAO,mBAAkB;AAIhD,SAAQC,UAAU,QAAO,cAAa;AAoBtC;;;;;CAKC,GACD,SAASC,wBAAwBC,sBAAqC;IACpE,IAAIA,2BAA2B,MAAM;QACnC,MAAM,IAAIC,MACR,yEACE,8EACA;IAEN;IACA,OAAOD;AACT;AAEA,2EAA2E,GAC3E,OAAO,eAAeE,qBAAqBC,OAA6B;IACtE,MAAM,EAACC,GAAG,EAAEC,OAAO,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,KAAK,EAAC,GAAGL;IAC/C,MAAMM,UAAU,MAAMZ,gBAAgBD,KAAKc,IAAI,CAACN,KAAK;IAErD,OAAON,WAAW;QAChB,GAAIQ,QACA;YACE,0EAA0E;YAC1E,oDAAoD;YACpD,GAAID,QAAQM,aAAa,GAAG;gBAACC,UAAUP,QAAQM,aAAa;YAAA,IAAI,CAAC,CAAC;YAClEL,OAAO;QACT,IACA;YACEA,OAAO;YACPO,kBAAkBd,wBAAwBM,QAAQL,sBAAsB;QAC1E,CAAC;QACLS;QACAF;QACAC;QACAM,SAASV;IACX;AACF"}
1
+ {"version":3,"sources":["../../../../src/actions/build/vite/workbench-vite-plugins.ts"],"sourcesContent":["// The build-facing entry of the workbench federation stack: turn a workbench\n// app's build inputs into the Vite plugins that produce its module-federation\n// remote. `@sanity/cli-build`'s `getViteConfig` calls this instead of\n// assembling `federation`'s options itself, so the discriminated app-vs-studio\n// option shape, the no-app-view rule, and the studio-config requirement all\n// live here next to `federation` — the build package just hands over its inputs.\n\nimport path from 'node:path'\n\nimport {readPackageJson} from '@sanity/cli-core'\nimport {type PluginOption} from 'vite'\n\nimport {type WorkbenchExposes} from '../../../resolveWorkbenchApp.js'\nimport {federation} from './plugin.js'\nimport {sanityAppId} from './plugins/plugin-sanity-app-id.js'\n\ninterface WorkbenchViteOptions {\n /** Project root — read for the federation remote name, and the plugin workDir. */\n cwd: string\n /**\n * Build entry paths relative to the federation runtime dir. `relativeEntry` is\n * the app's `entry` (null for a dock-only app with no app view);\n * `relativeConfigLocation` is the studio's `sanity.config.*` (null when absent).\n */\n entries: {relativeConfigLocation: string | null; relativeEntry: string | null}\n\n /** The app's bus identity, stamped into its modules for `@sanity/runtime`. */\n appId?: string\n\n exposes?: WorkbenchExposes\n /** App (vs studio) build — selects the discriminated federation option shape. */\n isApp?: boolean\n}\n\n/**\n * A workbench studio renders from its `sanity.config.*`, so the build needs one.\n * An explicit `applicationType: 'studio'` wins over detection, so a studio can\n * reach here with no config file — fail with the fix rather than a cryptic build\n * error downstream.\n */\nfunction requireStudioConfigPath(relativeConfigLocation: string | null): string {\n if (relativeConfigLocation === null) {\n throw new Error(\n 'Workbench studios need a sanity.config.js or sanity.config.ts file. ' +\n \"Add one, or remove `applicationType: 'studio'` from `unstable_defineApp` \" +\n 'to let the CLI infer the application type.',\n )\n }\n return relativeConfigLocation\n}\n\n/** Build the Vite plugins for a workbench app's module-federation remote. */\nexport async function workbenchVitePlugins(options: WorkbenchViteOptions): Promise<PluginOption> {\n const {appId, cwd, entries, exposes, isApp} = options\n const pkgJson = await readPackageJson(path.join(cwd, 'package.json'))\n\n const federationPlugin = federation({\n ...(isApp\n ? {\n // `null` relativeEntry (a branded app with no `entry`) → omit `appEntry`,\n // so the remote exposes no `./App`, only its views.\n ...(entries.relativeEntry ? {appEntry: entries.relativeEntry} : {}),\n isApp: true as const,\n }\n : {\n isApp: false as const,\n studioConfigPath: requireStudioConfigPath(entries.relativeConfigLocation),\n }),\n exposes,\n pkgJson,\n workDir: cwd,\n })\n\n return appId === undefined ? federationPlugin : [federationPlugin, sanityAppId(appId)]\n}\n"],"names":["path","readPackageJson","federation","sanityAppId","requireStudioConfigPath","relativeConfigLocation","Error","workbenchVitePlugins","options","appId","cwd","entries","exposes","isApp","pkgJson","join","federationPlugin","relativeEntry","appEntry","studioConfigPath","workDir","undefined"],"mappings":"AAAA,6EAA6E;AAC7E,8EAA8E;AAC9E,sEAAsE;AACtE,+EAA+E;AAC/E,4EAA4E;AAC5E,iFAAiF;AAEjF,OAAOA,UAAU,YAAW;AAE5B,SAAQC,eAAe,QAAO,mBAAkB;AAIhD,SAAQC,UAAU,QAAO,cAAa;AACtC,SAAQC,WAAW,QAAO,oCAAmC;AAoB7D;;;;;CAKC,GACD,SAASC,wBAAwBC,sBAAqC;IACpE,IAAIA,2BAA2B,MAAM;QACnC,MAAM,IAAIC,MACR,yEACE,8EACA;IAEN;IACA,OAAOD;AACT;AAEA,2EAA2E,GAC3E,OAAO,eAAeE,qBAAqBC,OAA6B;IACtE,MAAM,EAACC,KAAK,EAAEC,GAAG,EAAEC,OAAO,EAAEC,OAAO,EAAEC,KAAK,EAAC,GAAGL;IAC9C,MAAMM,UAAU,MAAMb,gBAAgBD,KAAKe,IAAI,CAACL,KAAK;IAErD,MAAMM,mBAAmBd,WAAW;QAClC,GAAIW,QACA;YACE,0EAA0E;YAC1E,oDAAoD;YACpD,GAAIF,QAAQM,aAAa,GAAG;gBAACC,UAAUP,QAAQM,aAAa;YAAA,IAAI,CAAC,CAAC;YAClEJ,OAAO;QACT,IACA;YACEA,OAAO;YACPM,kBAAkBf,wBAAwBO,QAAQN,sBAAsB;QAC1E,CAAC;QACLO;QACAE;QACAM,SAASV;IACX;IAEA,OAAOD,UAAUY,YAAYL,mBAAmB;QAACA;QAAkBb,YAAYM;KAAO;AACxF"}
@@ -0,0 +1,5 @@
1
+ // The app endpoints (applications + installations) live behind the
2
+ // experimental `vX` version; a dated version returns 501.
3
+ export const APP_WORKBENCH_API_VERSION = 'vX';
4
+
5
+ //# sourceMappingURL=apiVersion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/deploy/apiVersion.ts"],"sourcesContent":["// The app endpoints (applications + installations) live behind the\n// experimental `vX` version; a dated version returns 501.\nexport const APP_WORKBENCH_API_VERSION = 'vX'\n"],"names":["APP_WORKBENCH_API_VERSION"],"mappings":"AAAA,mEAAmE;AACnE,0DAA0D;AAC1D,OAAO,MAAMA,4BAA4B,KAAI"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The interface records deploy sends: the app view (only when `exposesAppView`),
3
+ * every view, and every service.
4
+ * @internal
5
+ */ export function buildExposes(exposes, { appName, appTitle, exposesAppView, version }) {
6
+ const toRecord = (prefix, decl)=>({
7
+ moduleId: `${prefix}/${decl.name}`,
8
+ name: decl.name,
9
+ title: decl.title ?? decl.name,
10
+ type: decl.type,
11
+ version
12
+ });
13
+ const records = [];
14
+ if (exposesAppView) {
15
+ records.push({
16
+ moduleId: 'App',
17
+ name: appName,
18
+ title: appTitle,
19
+ type: 'app',
20
+ version
21
+ });
22
+ }
23
+ for (const view of exposes.views ?? [])records.push(toRecord('views', view));
24
+ for (const service of exposes.services ?? [])records.push(toRecord('services', service));
25
+ return records;
26
+ }
27
+ function summarizeExposeGroup(heading, items) {
28
+ const label = (item)=>item.title === item.name ? item.name : `${item.title} (${item.name})`;
29
+ return `${heading}:\n${items.map((item)=>` ${label(item)}: ${item.src}`).join('\n')}`;
30
+ }
31
+ /**
32
+ * The deploy summary of an app's exposes: the structured records (for `--json`)
33
+ * and one report line per non-empty group (for the human report).
34
+ * @internal
35
+ */ export function summarizeExposes({ services, views }) {
36
+ const toExpose = (decl)=>({
37
+ name: decl.name,
38
+ src: decl.src,
39
+ title: decl.title ?? decl.name,
40
+ type: decl.type
41
+ });
42
+ const viewExposes = (views ?? []).map((view)=>toExpose(view));
43
+ const serviceExposes = (services ?? []).map((service)=>toExpose(service));
44
+ const lines = [];
45
+ if (viewExposes.length > 0) lines.push(summarizeExposeGroup('Views', viewExposes));
46
+ if (serviceExposes.length > 0) lines.push(summarizeExposeGroup('Services', serviceExposes));
47
+ return {
48
+ exposes: [
49
+ ...viewExposes,
50
+ ...serviceExposes
51
+ ],
52
+ lines
53
+ };
54
+ }
55
+
56
+ //# sourceMappingURL=buildExposes.js.map
@@ -0,0 +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"}