@sanity/workbench-cli 1.10.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,9 +14,8 @@ declare type AssetSourceView = Extract<
14
14
  /**
15
15
  * The `build`/`start` id — a hash of the app's declared shape (its identity, not
16
16
  * its code), so the bundle inlined by `sanity build` and the registry entry
17
- * advertised by `sanity start` resolve to the same id. Hashed with the Web Crypto
18
- * API rather than `node:crypto` for parity with `resolveAppId`'s browser-safe
19
- * home. `sanity deploy` resolves its own id from the applications API.
17
+ * advertised by `sanity start` resolve to the same id. `sanity deploy` resolves
18
+ * its own id from the applications API.
20
19
  */
21
20
  export declare function buildAppId(app: ResolvedWorkbenchApp): Promise<string>;
22
21
 
@@ -21,6 +21,15 @@ declare type AssetSourceView = Extract<
21
21
  }
22
22
  >;
23
23
 
24
+ /**
25
+ * A resource a deployment may interact with, as Brett stores it. Per-deployment
26
+ * and forbidden for singletons (the server 400s).
27
+ */
28
+ export declare interface BrettAccess {
29
+ resourceId: string;
30
+ resourceType: "canvas" | "dashboard" | "dataset" | "media-library";
31
+ }
32
+
24
33
  /** A studio workspace as Brett stores it. */
25
34
  export declare interface BrettWorkspace {
26
35
  dataset: string;
@@ -57,13 +66,11 @@ export declare function createCoreApp(options: {
57
66
  }): Promise<CreatedApplication>;
58
67
 
59
68
  /**
60
- * A freshly created application record: its id, plus a way to undo the creation.
61
- * The caller builds and deploys with the id, and calls `rollback` if a later
62
- * step fails so the record isn't stranded at its slug.
69
+ * `rollback` undoes the creation, so a later failure leaves no record stranded at the slug.
63
70
  * @internal
64
71
  */
65
72
  export declare interface CreatedApplication {
66
- applicationId: string;
73
+ application: Application;
67
74
  rollback: () => Promise<void>;
68
75
  }
69
76
 
@@ -273,7 +280,7 @@ export declare function deployConfig(options: {
273
280
  }): Promise<void>;
274
281
 
275
282
  /** A view or service as the deploy report and `--json` output surface it. */
276
- export declare interface DeployedExpose {
283
+ export declare interface DeployedInterface {
277
284
  name: string;
278
285
  src: string;
279
286
  title: string;
@@ -292,6 +299,7 @@ export declare interface DeployedExpose {
292
299
  * @internal
293
300
  */
294
301
  export declare function deployWorkbenchApp(options: {
302
+ access?: readonly BrettAccess[];
295
303
  app: CliConfig["app"];
296
304
  applicationId: string;
297
305
  icon?: string;
@@ -374,16 +382,16 @@ export declare function summarizeConfig(config: {
374
382
  }): string;
375
383
 
376
384
  /**
377
- * The deploy summary of an app's exposes: the structured records (for `--json`)
378
- * and one report line per non-empty group (for the human report).
385
+ * One report line per non-empty group, alongside the records `--json` reports.
379
386
  * @internal
380
387
  */
381
388
  export declare function summarizeInterfaces({
382
389
  services,
383
390
  views,
384
391
  }: WorkbenchExposes): {
385
- exposes: DeployedExpose[];
386
392
  lines: string[];
393
+ services: DeployedInterface[];
394
+ views: DeployedInterface[];
387
395
  };
388
396
 
389
397
  /** The `tile` variant of an app's `views`. @public */
@@ -394,6 +402,26 @@ declare type TileView = Extract<
394
402
  }
395
403
  >;
396
404
 
405
+ /**
406
+ * Gated once so a plain app can't pick up a stray key: without a workbench
407
+ * there is nothing to contribute. `views` and `services` travel together.
408
+ */
409
+ export declare function toWorkbenchPayload(
410
+ workbench: DeployableWorkbenchApp | null,
411
+ {
412
+ config,
413
+ interfaces,
414
+ title,
415
+ }: {
416
+ config?: string;
417
+ interfaces: {
418
+ services: DeployedInterface[];
419
+ views: DeployedInterface[];
420
+ } | null;
421
+ title: string;
422
+ },
423
+ ): Partial<WorkbenchDeployPayload>;
424
+
397
425
  declare type ViewDeploymentPayload = z.infer<
398
426
  typeof viewDeploymentPayloadSchema
399
427
  >;
@@ -443,6 +471,18 @@ declare const WORKBENCH_APP: unique symbol;
443
471
  declare type WorkbenchApp = DefineAppResult &
444
472
  z.output<typeof DefineAppInputSchema>;
445
473
 
474
+ /** What a workbench app contributes to a deploy payload. */
475
+ export declare interface WorkbenchDeployPayload {
476
+ slug: string;
477
+ title: string;
478
+ /** Media-library config summary. */
479
+ config?: string;
480
+ isSingleton?: boolean;
481
+ services?: DeployedInterface[];
482
+ views?: DeployedInterface[];
483
+ visibility?: string;
484
+ }
485
+
446
486
  /**
447
487
  * Bundled so adding a declaration family touches this type and the artifact
448
488
  * expanders, not every hop of build/dev plumbing in between.
@@ -1,5 +1,6 @@
1
1
  export { checkBuiltOutput } from '../actions/deploy/checkBuiltOutput.js';
2
2
  export { deployConfig, resolveInstallationId, summarizeConfig } from '../actions/deploy/deployConfig.js';
3
+ export { toWorkbenchPayload } from '../actions/deploy/deployPayload.js';
3
4
  export { createCoreApp, createStudio, deployWorkbenchApp } from '../actions/deploy/deployWorkbenchApp.js';
4
5
  export { getWorkbench } from '../actions/deploy/getWorkbench.js';
5
6
  export { summarizeInterfaces } from '../actions/deploy/summarizeInterfaces.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/_exports/deploy.ts"],"sourcesContent":["export {checkBuiltOutput} from '../actions/deploy/checkBuiltOutput.js'\nexport {\n deployConfig,\n resolveInstallationId,\n summarizeConfig,\n} from '../actions/deploy/deployConfig.js'\nexport {\n createCoreApp,\n type CreatedApplication,\n createStudio,\n deployWorkbenchApp,\n} from '../actions/deploy/deployWorkbenchApp.js'\nexport {getWorkbench} from '../actions/deploy/getWorkbench.js'\nexport {type DeployedExpose, summarizeInterfaces} from '../actions/deploy/summarizeInterfaces.js'\nexport {\n type Application,\n type BrettWorkspace,\n getApplication,\n getApplicationUrl,\n getWorkbenchUrl,\n listApplications,\n} from '../services/applications.js'\n"],"names":["checkBuiltOutput","deployConfig","resolveInstallationId","summarizeConfig","createCoreApp","createStudio","deployWorkbenchApp","getWorkbench","summarizeInterfaces","getApplication","getApplicationUrl","getWorkbenchUrl","listApplications"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,wCAAuC;AACtE,SACEC,YAAY,EACZC,qBAAqB,EACrBC,eAAe,QACV,oCAAmC;AAC1C,SACEC,aAAa,EAEbC,YAAY,EACZC,kBAAkB,QACb,0CAAyC;AAChD,SAAQC,YAAY,QAAO,oCAAmC;AAC9D,SAA6BC,mBAAmB,QAAO,2CAA0C;AACjG,SAGEC,cAAc,EACdC,iBAAiB,EACjBC,eAAe,EACfC,gBAAgB,QACX,8BAA6B"}
1
+ {"version":3,"sources":["../../src/_exports/deploy.ts"],"sourcesContent":["export {checkBuiltOutput} from '../actions/deploy/checkBuiltOutput.js'\nexport {\n deployConfig,\n resolveInstallationId,\n summarizeConfig,\n} from '../actions/deploy/deployConfig.js'\nexport {toWorkbenchPayload, type WorkbenchDeployPayload} from '../actions/deploy/deployPayload.js'\nexport {\n createCoreApp,\n type CreatedApplication,\n createStudio,\n deployWorkbenchApp,\n} from '../actions/deploy/deployWorkbenchApp.js'\nexport {getWorkbench} from '../actions/deploy/getWorkbench.js'\nexport {type DeployedInterface, summarizeInterfaces} from '../actions/deploy/summarizeInterfaces.js'\nexport {\n type Application,\n type BrettAccess,\n type BrettWorkspace,\n getApplication,\n getApplicationUrl,\n getWorkbenchUrl,\n listApplications,\n} from '../services/applications.js'\n"],"names":["checkBuiltOutput","deployConfig","resolveInstallationId","summarizeConfig","toWorkbenchPayload","createCoreApp","createStudio","deployWorkbenchApp","getWorkbench","summarizeInterfaces","getApplication","getApplicationUrl","getWorkbenchUrl","listApplications"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,wCAAuC;AACtE,SACEC,YAAY,EACZC,qBAAqB,EACrBC,eAAe,QACV,oCAAmC;AAC1C,SAAQC,kBAAkB,QAAoC,qCAAoC;AAClG,SACEC,aAAa,EAEbC,YAAY,EACZC,kBAAkB,QACb,0CAAyC;AAChD,SAAQC,YAAY,QAAO,oCAAmC;AAC9D,SAAgCC,mBAAmB,QAAO,2CAA0C;AACpG,SAIEC,cAAc,EACdC,iBAAiB,EACjBC,eAAe,EACfC,gBAAgB,QACX,8BAA6B"}
@@ -290,17 +290,6 @@ export declare type PanelViewProps = ViewComponentBaseProps<{
290
290
  type: "panel";
291
291
  }>;
292
292
 
293
- /**
294
- * The dev id for a workbench app — the address the server bound. `sanity dev`
295
- * keys on where the app is served so a running app can't collide with its
296
- * deployed twin. Sync and dependency-free: it's re-exported from the package's
297
- * browser-facing entry, so it must not pull in `node:crypto`.
298
- */
299
- export declare function resolveAppId(source: {
300
- host: string;
301
- port: number;
302
- }): string;
303
-
304
293
  /** @internal */
305
294
  declare const SERVICE_CONTRACT_VERSION = 1;
306
295
 
@@ -1,4 +1,3 @@
1
- export { resolveAppId } from '../appId.js';
2
1
  export { isWorkbenchApp, unstable_defineApp, unstable_defineMediaLibrary } from '../defineApp.js';
3
2
  export { unstable_defineService } from '../defineService.js';
4
3
  export { unstable_defineView } from '../defineView.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/_exports/index.ts"],"sourcesContent":["export {resolveAppId} from '../appId.js'\nexport type {InterfaceType, ServiceType, TileSize} 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 AssetSourceComponent,\n AssetSourceViewComponents,\n DefinedView,\n PanelComponent,\n PanelViewComponents,\n PanelViewProps,\n TileComponent,\n TileViewComponents,\n TileViewProps,\n ViewComponentsByType,\n} from '../defineView.js'\n"],"names":["resolveAppId","isWorkbenchApp","unstable_defineApp","unstable_defineMediaLibrary","unstable_defineService","unstable_defineView"],"mappings":"AAAA,SAAQA,YAAY,QAAO,cAAa;AAExC,SAAQC,cAAc,EAAEC,kBAAkB,EAAEC,2BAA2B,QAAO,kBAAiB;AAS/F,SAAQC,sBAAsB,QAAO,sBAAqB;AAO1D,SAAQC,mBAAmB,QAAO,mBAAkB"}
1
+ {"version":3,"sources":["../../src/_exports/index.ts"],"sourcesContent":["export type {InterfaceType, ServiceType, TileSize} 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 AssetSourceComponent,\n AssetSourceViewComponents,\n DefinedView,\n PanelComponent,\n PanelViewComponents,\n PanelViewProps,\n TileComponent,\n TileViewComponents,\n TileViewProps,\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"}
@@ -12,6 +12,14 @@ declare type AssetSourceView = Extract<
12
12
  }
13
13
  >;
14
14
 
15
+ declare interface ConfigSnapshot {
16
+ id: string;
17
+ createdAt?: string;
18
+ deployedBy?: string;
19
+ /** Whether this snapshot is the one being served; at most one per installation. */
20
+ isActive?: boolean;
21
+ }
22
+
15
23
  /**
16
24
  * The undeploy adapter for workbench apps, mirroring what a workbench deploy
17
25
  * creates: apps that expose interfaces delete their Brett application (the
@@ -205,7 +213,7 @@ declare interface DeployableWorkbenchApp extends ResolvedWorkbenchApp {
205
213
  }
206
214
 
207
215
  /** A view or service as the deploy report and `--json` output surface it. */
208
- declare interface DeployedExpose {
216
+ declare interface DeployedInterface {
209
217
  name: string;
210
218
  src: string;
211
219
  title: string;
@@ -295,16 +303,11 @@ declare type WorkbenchApp = DefineAppResult &
295
303
  /** The workbench extension of the shared target; serializes into `--json` as-is. */
296
304
  declare type WorkbenchUndeployTarget =
297
305
  | (UndeployApplicationTarget & {
298
- /** Interfaces (views and services) registered by the application. */
299
- interfaces: DeployedExpose[];
306
+ services: DeployedInterface[];
307
+ views: DeployedInterface[];
300
308
  })
301
309
  | (UndeployConfigTarget & {
302
- /** The deployed config snapshots an undeploy deletes. */
303
- configs: {
304
- createdAt: string | null;
305
- deployedBy: string | null;
306
- id: string;
307
- }[];
310
+ configs: ConfigSnapshot[];
308
311
  });
309
312
 
310
313
  export {};
@@ -5,7 +5,7 @@ import { subdebug } from '@sanity/cli-core';
5
5
  import { pack } from 'tar-fs';
6
6
  import { getWorkbenchUrl } from '../../services/applications.js';
7
7
  import { createConfig, resolveSingletonInstallationId } from '../../services/installations.js';
8
- import { summarizeExposeGroup } from './summarizeInterfaces.js';
8
+ import { summarizeGroup } from './summarizeInterfaces.js';
9
9
  const debug = subdebug('deploy');
10
10
  /**
11
11
  * The org's active installation for an app type, or `undefined` when none is
@@ -31,7 +31,7 @@ const debug = subdebug('deploy');
31
31
  switch(config.appType){
32
32
  case 'media-library':
33
33
  {
34
- return summarizeExposeGroup('Media library fields', config.fields);
34
+ return summarizeGroup('Media library fields', config.fields);
35
35
  }
36
36
  default:
37
37
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/deployConfig.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip} from 'node:zlib'\n\nimport {type Output, subdebug} from '@sanity/cli-core'\nimport {pack} from 'tar-fs'\n\nimport {getWorkbenchUrl} from '../../services/applications.js'\nimport {createConfig, resolveSingletonInstallationId} from '../../services/installations.js'\nimport {summarizeExposeGroup} from './summarizeInterfaces.js'\n\nconst debug = subdebug('deploy')\n\n/**\n * The org's active installation for an app type, or `undefined` when none is\n * installed. Read-only, so `--dry-run` can report deployability.\n * @internal\n */\nexport async function resolveInstallationId(options: {\n appType: string\n organizationId: string\n}): Promise<string | undefined> {\n switch (options.appType) {\n case 'media-library': {\n return resolveSingletonInstallationId(options.organizationId, 'media-library')\n }\n default: {\n throw new Error(`Cannot create config for unknown app type: ${options.appType}`)\n }\n }\n}\n\n/**\n * A report heading and item list for a config; a media library's `fields` are\n * one of potentially many shapes.\n * @internal\n */\nexport function summarizeConfig(config: {\n appType: string\n fields: {name: string; src: string; title: string}[]\n}): string {\n switch (config.appType) {\n case 'media-library': {\n return summarizeExposeGroup('Media library fields', config.fields)\n }\n default: {\n throw new Error(`Cannot create config for unknown app type: ${config.appType}`)\n }\n }\n}\n\n/**\n * Upload the built module-federation remote to the installation as its config\n * snapshot. `installationId` is resolved by the caller so `--dry-run` never\n * reaches this mutating step.\n * @internal\n */\nexport async function deployConfig(options: {\n appType: string\n installationId: string\n organizationId: string\n output: Output\n sourceDir: string\n version: string\n}): Promise<void> {\n const {appType, installationId, organizationId, output, sourceDir, version} = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n await createConfig(installationId, {tarball, version})\n\n debug('Deployed config for app type: %s', appType)\n const url = getWorkbenchUrl(organizationId)\n output.log(`\\n🚀 ${styleText('bold', 'Success!')} Config deployed to ${styleText('cyan', url)}`)\n}\n"],"names":["basename","dirname","styleText","createGzip","subdebug","pack","getWorkbenchUrl","createConfig","resolveSingletonInstallationId","summarizeExposeGroup","debug","resolveInstallationId","options","appType","organizationId","Error","summarizeConfig","config","fields","deployConfig","installationId","output","sourceDir","version","tarball","entries","pipe","url","log"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAC3C,SAAQC,SAAS,QAAO,YAAW;AACnC,SAAQC,UAAU,QAAO,YAAW;AAEpC,SAAqBC,QAAQ,QAAO,mBAAkB;AACtD,SAAQC,IAAI,QAAO,SAAQ;AAE3B,SAAQC,eAAe,QAAO,iCAAgC;AAC9D,SAAQC,YAAY,EAAEC,8BAA8B,QAAO,kCAAiC;AAC5F,SAAQC,oBAAoB,QAAO,2BAA0B;AAE7D,MAAMC,QAAQN,SAAS;AAEvB;;;;CAIC,GACD,OAAO,eAAeO,sBAAsBC,OAG3C;IACC,OAAQA,QAAQC,OAAO;QACrB,KAAK;YAAiB;gBACpB,OAAOL,+BAA+BI,QAAQE,cAAc,EAAE;YAChE;QACA;YAAS;gBACP,MAAM,IAAIC,MAAM,CAAC,2CAA2C,EAAEH,QAAQC,OAAO,EAAE;YACjF;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASG,gBAAgBC,MAG/B;IACC,OAAQA,OAAOJ,OAAO;QACpB,KAAK;YAAiB;gBACpB,OAAOJ,qBAAqB,wBAAwBQ,OAAOC,MAAM;YACnE;QACA;YAAS;gBACP,MAAM,IAAIH,MAAM,CAAC,2CAA2C,EAAEE,OAAOJ,OAAO,EAAE;YAChF;IACF;AACF;AAEA;;;;;CAKC,GACD,OAAO,eAAeM,aAAaP,OAOlC;IACC,MAAM,EAACC,OAAO,EAAEO,cAAc,EAAEN,cAAc,EAAEO,MAAM,EAAEC,SAAS,EAAEC,OAAO,EAAC,GAAGX;IAC9E,MAAMY,UAAUnB,KAAKJ,QAAQqB,YAAY;QAACG,SAAS;YAACzB,SAASsB;SAAW;IAAA,GAAGI,IAAI,CAACvB;IAChF,MAAMI,aAAaa,gBAAgB;QAACI;QAASD;IAAO;IAEpDb,MAAM,oCAAoCG;IAC1C,MAAMc,MAAMrB,gBAAgBQ;IAC5BO,OAAOO,GAAG,CAAC,CAAC,KAAK,EAAE1B,UAAU,QAAQ,YAAY,oBAAoB,EAAEA,UAAU,QAAQyB,MAAM;AACjG"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/deployConfig.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip} from 'node:zlib'\n\nimport {type Output, subdebug} from '@sanity/cli-core'\nimport {pack} from 'tar-fs'\n\nimport {getWorkbenchUrl} from '../../services/applications.js'\nimport {createConfig, resolveSingletonInstallationId} from '../../services/installations.js'\nimport {summarizeGroup} from './summarizeInterfaces.js'\n\nconst debug = subdebug('deploy')\n\n/**\n * The org's active installation for an app type, or `undefined` when none is\n * installed. Read-only, so `--dry-run` can report deployability.\n * @internal\n */\nexport async function resolveInstallationId(options: {\n appType: string\n organizationId: string\n}): Promise<string | undefined> {\n switch (options.appType) {\n case 'media-library': {\n return resolveSingletonInstallationId(options.organizationId, 'media-library')\n }\n default: {\n throw new Error(`Cannot create config for unknown app type: ${options.appType}`)\n }\n }\n}\n\n/**\n * A report heading and item list for a config; a media library's `fields` are\n * one of potentially many shapes.\n * @internal\n */\nexport function summarizeConfig(config: {\n appType: string\n fields: {name: string; src: string; title: string}[]\n}): string {\n switch (config.appType) {\n case 'media-library': {\n return summarizeGroup('Media library fields', config.fields)\n }\n default: {\n throw new Error(`Cannot create config for unknown app type: ${config.appType}`)\n }\n }\n}\n\n/**\n * Upload the built module-federation remote to the installation as its config\n * snapshot. `installationId` is resolved by the caller so `--dry-run` never\n * reaches this mutating step.\n * @internal\n */\nexport async function deployConfig(options: {\n appType: string\n installationId: string\n organizationId: string\n output: Output\n sourceDir: string\n version: string\n}): Promise<void> {\n const {appType, installationId, organizationId, output, sourceDir, version} = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n await createConfig(installationId, {tarball, version})\n\n debug('Deployed config for app type: %s', appType)\n const url = getWorkbenchUrl(organizationId)\n output.log(`\\n🚀 ${styleText('bold', 'Success!')} Config deployed to ${styleText('cyan', url)}`)\n}\n"],"names":["basename","dirname","styleText","createGzip","subdebug","pack","getWorkbenchUrl","createConfig","resolveSingletonInstallationId","summarizeGroup","debug","resolveInstallationId","options","appType","organizationId","Error","summarizeConfig","config","fields","deployConfig","installationId","output","sourceDir","version","tarball","entries","pipe","url","log"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAC3C,SAAQC,SAAS,QAAO,YAAW;AACnC,SAAQC,UAAU,QAAO,YAAW;AAEpC,SAAqBC,QAAQ,QAAO,mBAAkB;AACtD,SAAQC,IAAI,QAAO,SAAQ;AAE3B,SAAQC,eAAe,QAAO,iCAAgC;AAC9D,SAAQC,YAAY,EAAEC,8BAA8B,QAAO,kCAAiC;AAC5F,SAAQC,cAAc,QAAO,2BAA0B;AAEvD,MAAMC,QAAQN,SAAS;AAEvB;;;;CAIC,GACD,OAAO,eAAeO,sBAAsBC,OAG3C;IACC,OAAQA,QAAQC,OAAO;QACrB,KAAK;YAAiB;gBACpB,OAAOL,+BAA+BI,QAAQE,cAAc,EAAE;YAChE;QACA;YAAS;gBACP,MAAM,IAAIC,MAAM,CAAC,2CAA2C,EAAEH,QAAQC,OAAO,EAAE;YACjF;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASG,gBAAgBC,MAG/B;IACC,OAAQA,OAAOJ,OAAO;QACpB,KAAK;YAAiB;gBACpB,OAAOJ,eAAe,wBAAwBQ,OAAOC,MAAM;YAC7D;QACA;YAAS;gBACP,MAAM,IAAIH,MAAM,CAAC,2CAA2C,EAAEE,OAAOJ,OAAO,EAAE;YAChF;IACF;AACF;AAEA;;;;;CAKC,GACD,OAAO,eAAeM,aAAaP,OAOlC;IACC,MAAM,EAACC,OAAO,EAAEO,cAAc,EAAEN,cAAc,EAAEO,MAAM,EAAEC,SAAS,EAAEC,OAAO,EAAC,GAAGX;IAC9E,MAAMY,UAAUnB,KAAKJ,QAAQqB,YAAY;QAACG,SAAS;YAACzB,SAASsB;SAAW;IAAA,GAAGI,IAAI,CAACvB;IAChF,MAAMI,aAAaa,gBAAgB;QAACI;QAASD;IAAO;IAEpDb,MAAM,oCAAoCG;IAC1C,MAAMc,MAAMrB,gBAAgBQ;IAC5BO,OAAOO,GAAG,CAAC,CAAC,KAAK,EAAE1B,UAAU,QAAQ,YAAY,oBAAoB,EAAEA,UAAU,QAAQyB,MAAM;AACjG"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Gated once so a plain app can't pick up a stray key: without a workbench
3
+ * there is nothing to contribute. `views` and `services` travel together.
4
+ */ export function toWorkbenchPayload(workbench, { config, interfaces, title }) {
5
+ if (!workbench) return {};
6
+ const { services, views } = interfaces ?? {
7
+ services: [],
8
+ views: []
9
+ };
10
+ return {
11
+ ...config ? {
12
+ config
13
+ } : {},
14
+ ...workbench.isSingleton === undefined ? {} : {
15
+ isSingleton: workbench.isSingleton
16
+ },
17
+ ...services.length > 0 || views.length > 0 ? {
18
+ services,
19
+ views
20
+ } : {},
21
+ slug: workbench.slug,
22
+ title,
23
+ ...workbench.visibility ? {
24
+ visibility: workbench.visibility
25
+ } : {}
26
+ };
27
+ }
28
+
29
+ //# sourceMappingURL=deployPayload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/deploy/deployPayload.ts"],"sourcesContent":["import {type DeployableWorkbenchApp} from './getWorkbench.js'\nimport {type DeployedInterface} from './summarizeInterfaces.js'\n\n/** What a workbench app contributes to a deploy payload. */\nexport interface WorkbenchDeployPayload {\n slug: string\n title: string\n\n /** Media-library config summary. */\n config?: string\n isSingleton?: boolean\n services?: DeployedInterface[]\n views?: DeployedInterface[]\n visibility?: string\n}\n\n/**\n * Gated once so a plain app can't pick up a stray key: without a workbench\n * there is nothing to contribute. `views` and `services` travel together.\n */\nexport function toWorkbenchPayload(\n workbench: DeployableWorkbenchApp | null,\n {\n config,\n interfaces,\n title,\n }: {\n config?: string\n interfaces: {services: DeployedInterface[]; views: DeployedInterface[]} | null\n title: string\n },\n): Partial<WorkbenchDeployPayload> {\n if (!workbench) return {}\n const {services, views} = interfaces ?? {services: [], views: []}\n return {\n ...(config ? {config} : {}),\n ...(workbench.isSingleton === undefined ? {} : {isSingleton: workbench.isSingleton}),\n ...(services.length > 0 || views.length > 0 ? {services, views} : {}),\n slug: workbench.slug,\n title,\n ...(workbench.visibility ? {visibility: workbench.visibility} : {}),\n }\n}\n"],"names":["toWorkbenchPayload","workbench","config","interfaces","title","services","views","isSingleton","undefined","length","slug","visibility"],"mappings":"AAgBA;;;CAGC,GACD,OAAO,SAASA,mBACdC,SAAwC,EACxC,EACEC,MAAM,EACNC,UAAU,EACVC,KAAK,EAKN;IAED,IAAI,CAACH,WAAW,OAAO,CAAC;IACxB,MAAM,EAACI,QAAQ,EAAEC,KAAK,EAAC,GAAGH,cAAc;QAACE,UAAU,EAAE;QAAEC,OAAO,EAAE;IAAA;IAChE,OAAO;QACL,GAAIJ,SAAS;YAACA;QAAM,IAAI,CAAC,CAAC;QAC1B,GAAID,UAAUM,WAAW,KAAKC,YAAY,CAAC,IAAI;YAACD,aAAaN,UAAUM,WAAW;QAAA,CAAC;QACnF,GAAIF,SAASI,MAAM,GAAG,KAAKH,MAAMG,MAAM,GAAG,IAAI;YAACJ;YAAUC;QAAK,IAAI,CAAC,CAAC;QACpEI,MAAMT,UAAUS,IAAI;QACpBN;QACA,GAAIH,UAAUU,UAAU,GAAG;YAACA,YAAYV,UAAUU,UAAU;QAAA,IAAI,CAAC,CAAC;IACpE;AACF"}
@@ -11,14 +11,14 @@ import { createApplication, createDeployment, deleteApplication, updateApplicati
11
11
  */ export async function createCoreApp(options) {
12
12
  const spin = spinner('Creating application...').start();
13
13
  try {
14
- const { id } = await createApplication({
14
+ const application = await createApplication({
15
15
  ...options,
16
16
  type: 'coreApp'
17
17
  });
18
18
  spin.succeed();
19
19
  return {
20
- applicationId: id,
21
- rollback: ()=>deleteApplication(id)
20
+ application,
21
+ rollback: ()=>deleteApplication(application.id)
22
22
  };
23
23
  } catch (error) {
24
24
  spin.fail();
@@ -31,14 +31,14 @@ import { createApplication, createDeployment, deleteApplication, updateApplicati
31
31
  */ export async function createStudio(options) {
32
32
  const spin = spinner('Creating studio...').start();
33
33
  try {
34
- const { id } = await createApplication({
34
+ const application = await createApplication({
35
35
  ...options,
36
36
  type: 'studio'
37
37
  });
38
38
  spin.succeed();
39
39
  return {
40
- applicationId: id,
41
- rollback: ()=>deleteApplication(id)
40
+ application,
41
+ rollback: ()=>deleteApplication(application.id)
42
42
  };
43
43
  } catch (error) {
44
44
  spin.fail();
@@ -56,7 +56,7 @@ import { createApplication, createDeployment, deleteApplication, updateApplicati
56
56
  * application once it has an active deployment.
57
57
  * @internal
58
58
  */ export async function deployWorkbenchApp(options) {
59
- const { app, applicationId, icon, isApp, isAutoUpdating, label = 'Deploying...', onDeployed, sourceDir, title, version, visibility, workspaces } = options;
59
+ const { access, app, applicationId, icon, isApp, isAutoUpdating, label = 'Deploying...', onDeployed, sourceDir, title, version, visibility, workspaces } = options;
60
60
  const tarball = pack(dirname(sourceDir), {
61
61
  entries: [
62
62
  basename(sourceDir)
@@ -65,6 +65,7 @@ import { createApplication, createDeployment, deleteApplication, updateApplicati
65
65
  const spin = spinner(label).start();
66
66
  try {
67
67
  await createDeployment({
68
+ access,
68
69
  applicationId,
69
70
  // Brett assigns the id and resolves modules by `moduleId`, so neither travels.
70
71
  interfaces: deriveInterfaces(app, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/deployWorkbenchApp.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {createGzip} from 'node:zlib'\n\nimport {type AppVisibility, type CliConfig} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {pack} from 'tar-fs'\n\nimport {deriveInterfaces} from '../../deriveInterfaces.js'\nimport {\n type BrettInterface,\n type BrettWorkspace,\n createApplication,\n createDeployment,\n deleteApplication,\n updateApplication,\n} from '../../services/applications.js'\n\n/**\n * A freshly created application record: its id, plus a way to undo the creation.\n * The caller builds and deploys with the id, and calls `rollback` if a later\n * step fails so the record isn't stranded at its slug.\n * @internal\n */\nexport interface CreatedApplication {\n applicationId: string\n rollback: () => Promise<void>\n}\n\n/**\n * Create a coreApp record (no deployment), so the CLI can build with its id\n * before shipping the first deployment. First deploy only.\n * @internal\n */\nexport async function createCoreApp(options: {\n isSingleton?: boolean\n organizationId: string\n slug: string\n title: string\n visibility?: AppVisibility\n}): Promise<CreatedApplication> {\n const spin = spinner('Creating application...').start()\n try {\n const {id} = await createApplication({...options, type: 'coreApp'})\n spin.succeed()\n return {applicationId: id, rollback: () => deleteApplication(id)}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n\n/**\n * Create a studio record (no deployment).\n * @internal\n */\nexport async function createStudio(options: {\n organizationId: string\n projectId: string | undefined\n slug: string\n title: string\n visibility?: AppVisibility\n}): Promise<CreatedApplication> {\n const spin = spinner('Creating studio...').start()\n try {\n const {id} = await createApplication({...options, type: 'studio'})\n spin.succeed()\n return {applicationId: id, rollback: () => deleteApplication(id)}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n\n/**\n * Ship a deployment to an already-created (or `deployment.appId`) application,\n * then sync its mutable metadata (`title`, and `icon`/`visibility` when set)\n * from config. The deploy endpoint ignores these, so a redeploy patches them\n * here alongside the new deployment.\n *\n * `onDeployed` fires the instant the deployment is live, before the metadata\n * sync — so a caller can disarm a create-time rollback that must not delete an\n * application once it has an active deployment.\n * @internal\n */\nexport async function deployWorkbenchApp(options: {\n app: CliConfig['app']\n applicationId: string\n icon?: string\n isApp: boolean\n isAutoUpdating: boolean\n label?: string\n onDeployed?: () => void\n sourceDir: string\n title: string\n version: string\n visibility?: AppVisibility\n workspaces?: readonly BrettWorkspace[]\n}): Promise<void> {\n const {\n app,\n applicationId,\n icon,\n isApp,\n isAutoUpdating,\n label = 'Deploying...',\n onDeployed,\n sourceDir,\n title,\n version,\n visibility,\n workspaces,\n } = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner(label).start()\n try {\n await createDeployment({\n applicationId,\n // Brett assigns the id and resolves modules by `moduleId`, so neither travels.\n interfaces: deriveInterfaces(app, {appTitle: title, isApp}).map(\n ({id: _id, src: _src, ...iface}): BrettInterface => ({...iface, version}),\n ),\n isAutoUpdating,\n tarball,\n version,\n workspaces,\n })\n onDeployed?.()\n await updateApplication(applicationId, {\n title,\n ...(icon ? {icon} : {}),\n ...(visibility ? {visibility} : {}),\n })\n spin.succeed()\n } catch (error) {\n spin.clear()\n throw error\n }\n}\n"],"names":["basename","dirname","createGzip","spinner","pack","deriveInterfaces","createApplication","createDeployment","deleteApplication","updateApplication","createCoreApp","options","spin","start","id","type","succeed","applicationId","rollback","error","fail","createStudio","deployWorkbenchApp","app","icon","isApp","isAutoUpdating","label","onDeployed","sourceDir","title","version","visibility","workspaces","tarball","entries","pipe","interfaces","appTitle","map","_id","src","_src","iface","clear"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAC3C,SAAQC,UAAU,QAAO,YAAW;AAGpC,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,IAAI,QAAO,SAAQ;AAE3B,SAAQC,gBAAgB,QAAO,4BAA2B;AAC1D,SAGEC,iBAAiB,EACjBC,gBAAgB,EAChBC,iBAAiB,EACjBC,iBAAiB,QACZ,iCAAgC;AAavC;;;;CAIC,GACD,OAAO,eAAeC,cAAcC,OAMnC;IACC,MAAMC,OAAOT,QAAQ,2BAA2BU,KAAK;IACrD,IAAI;QACF,MAAM,EAACC,EAAE,EAAC,GAAG,MAAMR,kBAAkB;YAAC,GAAGK,OAAO;YAAEI,MAAM;QAAS;QACjEH,KAAKI,OAAO;QACZ,OAAO;YAACC,eAAeH;YAAII,UAAU,IAAMV,kBAAkBM;QAAG;IAClE,EAAE,OAAOK,OAAO;QACdP,KAAKQ,IAAI;QACT,MAAMD;IACR;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeE,aAAaV,OAMlC;IACC,MAAMC,OAAOT,QAAQ,sBAAsBU,KAAK;IAChD,IAAI;QACF,MAAM,EAACC,EAAE,EAAC,GAAG,MAAMR,kBAAkB;YAAC,GAAGK,OAAO;YAAEI,MAAM;QAAQ;QAChEH,KAAKI,OAAO;QACZ,OAAO;YAACC,eAAeH;YAAII,UAAU,IAAMV,kBAAkBM;QAAG;IAClE,EAAE,OAAOK,OAAO;QACdP,KAAKQ,IAAI;QACT,MAAMD;IACR;AACF;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAeG,mBAAmBX,OAaxC;IACC,MAAM,EACJY,GAAG,EACHN,aAAa,EACbO,IAAI,EACJC,KAAK,EACLC,cAAc,EACdC,QAAQ,cAAc,EACtBC,UAAU,EACVC,SAAS,EACTC,KAAK,EACLC,OAAO,EACPC,UAAU,EACVC,UAAU,EACX,GAAGtB;IACJ,MAAMuB,UAAU9B,KAAKH,QAAQ4B,YAAY;QAACM,SAAS;YAACnC,SAAS6B;SAAW;IAAA,GAAGO,IAAI,CAAClC;IAEhF,MAAMU,OAAOT,QAAQwB,OAAOd,KAAK;IACjC,IAAI;QACF,MAAMN,iBAAiB;YACrBU;YACA,+EAA+E;YAC/EoB,YAAYhC,iBAAiBkB,KAAK;gBAACe,UAAUR;gBAAOL;YAAK,GAAGc,GAAG,CAC7D,CAAC,EAACzB,IAAI0B,GAAG,EAAEC,KAAKC,IAAI,EAAE,GAAGC,OAAM,GAAsB,CAAA;oBAAC,GAAGA,KAAK;oBAAEZ;gBAAO,CAAA;YAEzEL;YACAQ;YACAH;YACAE;QACF;QACAL;QACA,MAAMnB,kBAAkBQ,eAAe;YACrCa;YACA,GAAIN,OAAO;gBAACA;YAAI,IAAI,CAAC,CAAC;YACtB,GAAIQ,aAAa;gBAACA;YAAU,IAAI,CAAC,CAAC;QACpC;QACApB,KAAKI,OAAO;IACd,EAAE,OAAOG,OAAO;QACdP,KAAKgC,KAAK;QACV,MAAMzB;IACR;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/deployWorkbenchApp.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {createGzip} from 'node:zlib'\n\nimport {type AppVisibility, type CliConfig} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {pack} from 'tar-fs'\n\nimport {deriveInterfaces} from '../../deriveInterfaces.js'\nimport {\n type Application,\n type BrettAccess,\n type BrettInterface,\n type BrettWorkspace,\n createApplication,\n createDeployment,\n deleteApplication,\n updateApplication,\n} from '../../services/applications.js'\n\n/**\n * `rollback` undoes the creation, so a later failure leaves no record stranded at the slug.\n * @internal\n */\nexport interface CreatedApplication {\n application: Application\n rollback: () => Promise<void>\n}\n\n/**\n * Create a coreApp record (no deployment), so the CLI can build with its id\n * before shipping the first deployment. First deploy only.\n * @internal\n */\nexport async function createCoreApp(options: {\n isSingleton?: boolean\n organizationId: string\n slug: string\n title: string\n visibility?: AppVisibility\n}): Promise<CreatedApplication> {\n const spin = spinner('Creating application...').start()\n try {\n const application = await createApplication({...options, type: 'coreApp'})\n spin.succeed()\n return {application, rollback: () => deleteApplication(application.id)}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n\n/**\n * Create a studio record (no deployment).\n * @internal\n */\nexport async function createStudio(options: {\n organizationId: string\n projectId: string | undefined\n slug: string\n title: string\n visibility?: AppVisibility\n}): Promise<CreatedApplication> {\n const spin = spinner('Creating studio...').start()\n try {\n const application = await createApplication({...options, type: 'studio'})\n spin.succeed()\n return {application, rollback: () => deleteApplication(application.id)}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n\n/**\n * Ship a deployment to an already-created (or `deployment.appId`) application,\n * then sync its mutable metadata (`title`, and `icon`/`visibility` when set)\n * from config. The deploy endpoint ignores these, so a redeploy patches them\n * here alongside the new deployment.\n *\n * `onDeployed` fires the instant the deployment is live, before the metadata\n * sync — so a caller can disarm a create-time rollback that must not delete an\n * application once it has an active deployment.\n * @internal\n */\nexport async function deployWorkbenchApp(options: {\n access?: readonly BrettAccess[]\n app: CliConfig['app']\n applicationId: string\n icon?: string\n isApp: boolean\n isAutoUpdating: boolean\n label?: string\n onDeployed?: () => void\n sourceDir: string\n title: string\n version: string\n visibility?: AppVisibility\n workspaces?: readonly BrettWorkspace[]\n}): Promise<void> {\n const {\n access,\n app,\n applicationId,\n icon,\n isApp,\n isAutoUpdating,\n label = 'Deploying...',\n onDeployed,\n sourceDir,\n title,\n version,\n visibility,\n workspaces,\n } = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner(label).start()\n try {\n await createDeployment({\n access,\n applicationId,\n // Brett assigns the id and resolves modules by `moduleId`, so neither travels.\n interfaces: deriveInterfaces(app, {appTitle: title, isApp}).map(\n ({id: _id, src: _src, ...iface}): BrettInterface => ({...iface, version}),\n ),\n isAutoUpdating,\n tarball,\n version,\n workspaces,\n })\n onDeployed?.()\n await updateApplication(applicationId, {\n title,\n ...(icon ? {icon} : {}),\n ...(visibility ? {visibility} : {}),\n })\n spin.succeed()\n } catch (error) {\n spin.clear()\n throw error\n }\n}\n"],"names":["basename","dirname","createGzip","spinner","pack","deriveInterfaces","createApplication","createDeployment","deleteApplication","updateApplication","createCoreApp","options","spin","start","application","type","succeed","rollback","id","error","fail","createStudio","deployWorkbenchApp","access","app","applicationId","icon","isApp","isAutoUpdating","label","onDeployed","sourceDir","title","version","visibility","workspaces","tarball","entries","pipe","interfaces","appTitle","map","_id","src","_src","iface","clear"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAC3C,SAAQC,UAAU,QAAO,YAAW;AAGpC,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,IAAI,QAAO,SAAQ;AAE3B,SAAQC,gBAAgB,QAAO,4BAA2B;AAC1D,SAKEC,iBAAiB,EACjBC,gBAAgB,EAChBC,iBAAiB,EACjBC,iBAAiB,QACZ,iCAAgC;AAWvC;;;;CAIC,GACD,OAAO,eAAeC,cAAcC,OAMnC;IACC,MAAMC,OAAOT,QAAQ,2BAA2BU,KAAK;IACrD,IAAI;QACF,MAAMC,cAAc,MAAMR,kBAAkB;YAAC,GAAGK,OAAO;YAAEI,MAAM;QAAS;QACxEH,KAAKI,OAAO;QACZ,OAAO;YAACF;YAAaG,UAAU,IAAMT,kBAAkBM,YAAYI,EAAE;QAAC;IACxE,EAAE,OAAOC,OAAO;QACdP,KAAKQ,IAAI;QACT,MAAMD;IACR;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeE,aAAaV,OAMlC;IACC,MAAMC,OAAOT,QAAQ,sBAAsBU,KAAK;IAChD,IAAI;QACF,MAAMC,cAAc,MAAMR,kBAAkB;YAAC,GAAGK,OAAO;YAAEI,MAAM;QAAQ;QACvEH,KAAKI,OAAO;QACZ,OAAO;YAACF;YAAaG,UAAU,IAAMT,kBAAkBM,YAAYI,EAAE;QAAC;IACxE,EAAE,OAAOC,OAAO;QACdP,KAAKQ,IAAI;QACT,MAAMD;IACR;AACF;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAeG,mBAAmBX,OAcxC;IACC,MAAM,EACJY,MAAM,EACNC,GAAG,EACHC,aAAa,EACbC,IAAI,EACJC,KAAK,EACLC,cAAc,EACdC,QAAQ,cAAc,EACtBC,UAAU,EACVC,SAAS,EACTC,KAAK,EACLC,OAAO,EACPC,UAAU,EACVC,UAAU,EACX,GAAGxB;IACJ,MAAMyB,UAAUhC,KAAKH,QAAQ8B,YAAY;QAACM,SAAS;YAACrC,SAAS+B;SAAW;IAAA,GAAGO,IAAI,CAACpC;IAEhF,MAAMU,OAAOT,QAAQ0B,OAAOhB,KAAK;IACjC,IAAI;QACF,MAAMN,iBAAiB;YACrBgB;YACAE;YACA,+EAA+E;YAC/Ec,YAAYlC,iBAAiBmB,KAAK;gBAACgB,UAAUR;gBAAOL;YAAK,GAAGc,GAAG,CAC7D,CAAC,EAACvB,IAAIwB,GAAG,EAAEC,KAAKC,IAAI,EAAE,GAAGC,OAAM,GAAsB,CAAA;oBAAC,GAAGA,KAAK;oBAAEZ;gBAAO,CAAA;YAEzEL;YACAQ;YACAH;YACAE;QACF;QACAL;QACA,MAAMrB,kBAAkBgB,eAAe;YACrCO;YACA,GAAIN,OAAO;gBAACA;YAAI,IAAI,CAAC,CAAC;YACtB,GAAIQ,aAAa;gBAACA;YAAU,IAAI,CAAC,CAAC;QACpC;QACAtB,KAAKI,OAAO;IACd,EAAE,OAAOG,OAAO;QACdP,KAAKkC,KAAK;QACV,MAAM3B;IACR;AACF"}
@@ -2,31 +2,28 @@ const label = (item)=>item.title === item.name ? item.name : `${item.title} (${i
2
2
  /**
3
3
  * One `Title (name): src` report line per declared entry point.
4
4
  * @internal
5
- */ export function summarizeExposeGroup(heading, items) {
5
+ */ export function summarizeGroup(heading, items) {
6
6
  return `${heading}:\n${items.map((item)=>` ${label(item)}: ${item.src}`).join('\n')}`;
7
7
  }
8
8
  /**
9
- * The deploy summary of an app's exposes: the structured records (for `--json`)
10
- * and one report line per non-empty group (for the human report).
9
+ * One report line per non-empty group, alongside the records `--json` reports.
11
10
  * @internal
12
11
  */ export function summarizeInterfaces({ services, views }) {
13
- const toExpose = (decl)=>({
12
+ const toInterface = (decl)=>({
14
13
  name: decl.name,
15
14
  src: decl.src,
16
15
  title: decl.title,
17
16
  type: decl.type
18
17
  });
19
- const viewExposes = (views ?? []).map((view)=>toExpose(view));
20
- const serviceExposes = (services ?? []).map((service)=>toExpose(service));
18
+ const deployedViews = (views ?? []).map((view)=>toInterface(view));
19
+ const deployedServices = (services ?? []).map((service)=>toInterface(service));
21
20
  const lines = [];
22
- if (viewExposes.length > 0) lines.push(summarizeExposeGroup('Views', viewExposes));
23
- if (serviceExposes.length > 0) lines.push(summarizeExposeGroup('Services', serviceExposes));
21
+ if (deployedViews.length > 0) lines.push(summarizeGroup('Views', deployedViews));
22
+ if (deployedServices.length > 0) lines.push(summarizeGroup('Services', deployedServices));
24
23
  return {
25
- exposes: [
26
- ...viewExposes,
27
- ...serviceExposes
28
- ],
29
- lines
24
+ lines,
25
+ services: deployedServices,
26
+ views: deployedViews
30
27
  };
31
28
  }
32
29
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/summarizeInterfaces.ts"],"sourcesContent":["import {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\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 summarizeInterfaces({services, views}: WorkbenchExposes): {\n exposes: DeployedExpose[]\n lines: string[]\n} {\n const toExpose = (decl: DeployedExpose): DeployedExpose => ({\n name: decl.name,\n src: decl.src,\n title: decl.title,\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":["label","item","title","name","summarizeExposeGroup","heading","items","map","src","join","summarizeInterfaces","services","views","toExpose","decl","type","viewExposes","view","serviceExposes","service","lines","length","push","exposes"],"mappings":"AAUA,MAAMA,QAAQ,CAACC,OACbA,KAAKC,KAAK,KAAKD,KAAKE,IAAI,GAAGF,KAAKE,IAAI,GAAG,GAAGF,KAAKC,KAAK,CAAC,EAAE,EAAED,KAAKE,IAAI,CAAC,CAAC,CAAC;AAEvE;;;CAGC,GACD,OAAO,SAASC,qBACdC,OAAe,EACfC,KAA4D;IAE5D,OAAO,GAAGD,QAAQ,GAAG,EAAEC,MAAMC,GAAG,CAAC,CAACN,OAAS,CAAC,EAAE,EAAED,MAAMC,MAAM,EAAE,EAAEA,KAAKO,GAAG,EAAE,EAAEC,IAAI,CAAC,OAAO;AAC1F;AAEA;;;;CAIC,GACD,OAAO,SAASC,oBAAoB,EAACC,QAAQ,EAAEC,KAAK,EAAmB;IAIrE,MAAMC,WAAW,CAACC,OAA0C,CAAA;YAC1DX,MAAMW,KAAKX,IAAI;YACfK,KAAKM,KAAKN,GAAG;YACbN,OAAOY,KAAKZ,KAAK;YACjBa,MAAMD,KAAKC,IAAI;QACjB,CAAA;IACA,MAAMC,cAAc,AAACJ,CAAAA,SAAS,EAAE,AAAD,EAAGL,GAAG,CAAC,CAACU,OAASJ,SAASI;IACzD,MAAMC,iBAAiB,AAACP,CAAAA,YAAY,EAAE,AAAD,EAAGJ,GAAG,CAAC,CAACY,UAAYN,SAASM;IAElE,MAAMC,QAAkB,EAAE;IAC1B,IAAIJ,YAAYK,MAAM,GAAG,GAAGD,MAAME,IAAI,CAAClB,qBAAqB,SAASY;IACrE,IAAIE,eAAeG,MAAM,GAAG,GAAGD,MAAME,IAAI,CAAClB,qBAAqB,YAAYc;IAC3E,OAAO;QAACK,SAAS;eAAIP;eAAgBE;SAAe;QAAEE;IAAK;AAC7D"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/summarizeInterfaces.ts"],"sourcesContent":["import {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\n\n/** A view or service as the deploy report and `--json` output surface it. */\nexport interface DeployedInterface {\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 summarizeGroup(\n heading: string,\n items: readonly {name: string; src: string; title: string}[],\n): string {\n return `${heading}:\\n${items.map((item) => ` ${label(item)}: ${item.src}`).join('\\n')}`\n}\n\n/**\n * One report line per non-empty group, alongside the records `--json` reports.\n * @internal\n */\nexport function summarizeInterfaces({services, views}: WorkbenchExposes): {\n lines: string[]\n services: DeployedInterface[]\n views: DeployedInterface[]\n} {\n const toInterface = (decl: DeployedInterface): DeployedInterface => ({\n name: decl.name,\n src: decl.src,\n title: decl.title,\n type: decl.type,\n })\n const deployedViews = (views ?? []).map((view) => toInterface(view))\n const deployedServices = (services ?? []).map((service) => toInterface(service))\n\n const lines: string[] = []\n if (deployedViews.length > 0) lines.push(summarizeGroup('Views', deployedViews))\n if (deployedServices.length > 0) lines.push(summarizeGroup('Services', deployedServices))\n return {lines, services: deployedServices, views: deployedViews}\n}\n"],"names":["label","item","title","name","summarizeGroup","heading","items","map","src","join","summarizeInterfaces","services","views","toInterface","decl","type","deployedViews","view","deployedServices","service","lines","length","push"],"mappings":"AAUA,MAAMA,QAAQ,CAACC,OACbA,KAAKC,KAAK,KAAKD,KAAKE,IAAI,GAAGF,KAAKE,IAAI,GAAG,GAAGF,KAAKC,KAAK,CAAC,EAAE,EAAED,KAAKE,IAAI,CAAC,CAAC,CAAC;AAEvE;;;CAGC,GACD,OAAO,SAASC,eACdC,OAAe,EACfC,KAA4D;IAE5D,OAAO,GAAGD,QAAQ,GAAG,EAAEC,MAAMC,GAAG,CAAC,CAACN,OAAS,CAAC,EAAE,EAAED,MAAMC,MAAM,EAAE,EAAEA,KAAKO,GAAG,EAAE,EAAEC,IAAI,CAAC,OAAO;AAC1F;AAEA;;;CAGC,GACD,OAAO,SAASC,oBAAoB,EAACC,QAAQ,EAAEC,KAAK,EAAmB;IAKrE,MAAMC,cAAc,CAACC,OAAgD,CAAA;YACnEX,MAAMW,KAAKX,IAAI;YACfK,KAAKM,KAAKN,GAAG;YACbN,OAAOY,KAAKZ,KAAK;YACjBa,MAAMD,KAAKC,IAAI;QACjB,CAAA;IACA,MAAMC,gBAAgB,AAACJ,CAAAA,SAAS,EAAE,AAAD,EAAGL,GAAG,CAAC,CAACU,OAASJ,YAAYI;IAC9D,MAAMC,mBAAmB,AAACP,CAAAA,YAAY,EAAE,AAAD,EAAGJ,GAAG,CAAC,CAACY,UAAYN,YAAYM;IAEvE,MAAMC,QAAkB,EAAE;IAC1B,IAAIJ,cAAcK,MAAM,GAAG,GAAGD,MAAME,IAAI,CAAClB,eAAe,SAASY;IACjE,IAAIE,iBAAiBG,MAAM,GAAG,GAAGD,MAAME,IAAI,CAAClB,eAAe,YAAYc;IACvE,OAAO;QAACE;QAAOT,UAAUO;QAAkBN,OAAOI;IAAa;AACjE"}
@@ -1,10 +1,10 @@
1
1
  import { getCliConfigUncached } from '@sanity/cli-core';
2
- import { resolveAppId } from '../../appId.js';
2
+ import { isWorkbenchApp } from '../../defineApp.js';
3
3
  import { deriveInterfaces } from '../../deriveInterfaces.js';
4
4
  import { formatWorkbenchAppErrors, validateWorkbenchApp } from '../../validateWorkbenchApp.js';
5
5
  import { deriveConfigs } from './deriveConfigs.js';
6
6
  import { trackExposesSet } from './exposesSetId.js';
7
- import { registerDevServer } from './registry.js';
7
+ import { getRegisteredServers, registerDevServer } from './registry.js';
8
8
  import { startDevManifestWatcher } from './startDevManifestWatcher.js';
9
9
  /**
10
10
  * Log any config validation errors without aborting. Unlike build and deploy,
@@ -13,7 +13,6 @@ import { startDevManifestWatcher } from './startDevManifestWatcher.js';
13
13
  */ function reportConfigErrors(app, output) {
14
14
  const errors = validateWorkbenchApp(app);
15
15
  if (errors.length === 0) return;
16
- // `output.error` exits the process; `warn` keeps the dev server alive.
17
16
  output.warn(formatWorkbenchAppErrors(errors));
18
17
  }
19
18
  /** The address the server actually bound — the live socket, which can differ from the configured port under non-strict ports. */ function serverAddress(server) {
@@ -38,16 +37,20 @@ import { startDevManifestWatcher } from './startDevManifestWatcher.js';
38
37
  isApp
39
38
  });
40
39
  const configs = await deriveConfigs(cliConfig.app);
40
+ const id = isWorkbenchApp(cliConfig.app) ? cliConfig.app.slug : undefined;
41
+ const devServer = id ? getRegisteredServers().find((server)=>server.id === id) : undefined;
42
+ if (id && devServer) {
43
+ output.error(`The app "${id}" is already served by another dev server running on port ${devServer.port}, ` + "so the workbench can't tell them apart and this one stays out of it. " + 'Stop that server, or give this app its own `slug` in sanity.cli.ts.', {
44
+ exit: false
45
+ });
46
+ return {
47
+ close: async ()=>{}
48
+ };
49
+ }
41
50
  const registration = registerDevServer({
42
51
  configs,
43
52
  host: appHost,
44
- // Keyed by where it's served (not the deployment id), so a running app can't
45
- // collide with its deployed twin — on the configured port, not the bound one,
46
- // to match `__SANITY_APP_ID__`, compiled before any non-strict shift.
47
- id: resolveAppId({
48
- host: appHost,
49
- port: server.config.server.port ?? appPort
50
- }),
53
+ id,
51
54
  interfaces,
52
55
  port: appPort,
53
56
  projectId: cliConfig?.api?.projectId,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/dev/startDevServerRegistration.ts"],"sourcesContent":["import {type CliConfig, getCliConfigUncached, type Output} from '@sanity/cli-core'\nimport {type ViteDevServer} from 'vite'\n\nimport {resolveAppId} from '../../appId.js'\nimport {deriveInterfaces} from '../../deriveInterfaces.js'\nimport {formatWorkbenchAppErrors, validateWorkbenchApp} from '../../validateWorkbenchApp.js'\nimport {deriveConfigs} from './deriveConfigs.js'\nimport {trackExposesSet} from './exposesSetId.js'\nimport {type DevServerManifest, registerDevServer} from './registry.js'\nimport {startDevManifestWatcher} from './startDevManifestWatcher.js'\n\ninterface DevServerRegistrationOptions {\n cliConfig: CliConfig\n /**\n * Extract the project manifest to inline into the registry. The caller owns the\n * studio-vs-app split (manifest formats are CLI-domain); registration re-derives\n * the interface set alongside it.\n */\n extractManifest: (params: {\n configPath: string\n workDir: string\n }) => Promise<DevServerManifest['manifest']>\n isApp: boolean\n output: Output\n server: ViteDevServer\n workDir: string\n\n /**\n * Rebuild the app's federation remote when its interface set changes, awaited\n * *before* the registry patch — the patch reloads the workbench page, which must\n * re-fetch a remote that already exposes the new interface. Resolves with the\n * recreated server so the entry gets its actual address (non-strict ports may\n * shift it); must reject if the restart produces no server, so the set stays\n * uncommitted and the next save retries instead of advertising a dead port.\n */\n onInterfaceSetChange?: () => Promise<ViteDevServer>\n}\n\ninterface DevServerRegistrationHandle {\n close: () => Promise<void>\n}\n\n/**\n * Log any config validation errors without aborting. Unlike build and deploy,\n * dev stays up on an invalid config so the author sees the errors and fixes them\n * live on the next save.\n */\nfunction reportConfigErrors(app: CliConfig['app'], output: Output): void {\n const errors = validateWorkbenchApp(app)\n if (errors.length === 0) return\n // `output.error` exits the process; `warn` keeps the dev server alive.\n output.warn(formatWorkbenchAppErrors(errors))\n}\n\n/** The address the server actually bound — the live socket, which can differ from the configured port under non-strict ports. */\nfunction serverAddress(server: ViteDevServer) {\n const resolvedHost = server.config.server.host\n const addr = server.httpServer?.address()\n return {\n host: typeof resolvedHost === 'string' ? resolvedHost : 'localhost',\n port: typeof addr === 'object' && addr ? addr.port : server.config.server.port,\n }\n}\n\n/**\n * Register the dev server in the registry and watch its config for manifest +\n * interface changes. The workbench reads the entry to locate and render the\n * server; the watcher keeps it current as `sanity.cli.ts` is edited.\n */\nexport async function startDevServerRegistration(\n options: DevServerRegistrationOptions,\n): Promise<DevServerRegistrationHandle> {\n const {cliConfig, extractManifest, isApp, onInterfaceSetChange, output, server, workDir} = options\n\n const {host: appHost, port: appPort} = serverAddress(server)\n\n reportConfigErrors(cliConfig.app, output)\n\n // Forwarded alongside (not inside) the manifest so the workbench renders local\n // panels/workers and reads the configs without a deploy.\n const interfaces = deriveInterfaces(cliConfig.app, {isApp})\n const configs = await deriveConfigs(cliConfig.app)\n\n const registration = registerDevServer({\n configs,\n host: appHost,\n // Keyed by where it's served (not the deployment id), so a running app can't\n // collide with its deployed twin on the configured port, not the bound one,\n // to match `__SANITY_APP_ID__`, compiled before any non-strict shift.\n id: resolveAppId({host: appHost, port: server.config.server.port ?? appPort}),\n interfaces,\n port: appPort,\n projectId: cliConfig?.api?.projectId,\n type: isApp ? 'coreApp' : 'studio',\n workDir,\n })\n\n const exposesSet = trackExposesSet({configs, interfaces})\n\n const watcher = await startDevManifestWatcher({\n // Re-derive every pass (don't omit): the registry patch is a shallow merge,\n // so omitting would wipe the registered set.\n extract: async (params) => {\n const app = (await getCliConfigUncached(params.workDir)).app\n reportConfigErrors(app, output)\n return {\n configs: await deriveConfigs(app),\n interfaces: deriveInterfaces(app, {isApp}),\n manifest: await extractManifest(params),\n }\n },\n // A studio's root resolves to `sanity.config.*` but its interfaces live in\n // `sanity.cli.*` — watch that too. Apps already root at `sanity.cli.*`.\n extraWatchFilenames: isApp ? undefined : ['sanity.cli.js', 'sanity.cli.ts'],\n output,\n update: async (patch) => {\n if (\n !exposesSet.changed({\n configs: patch.configs,\n interfaces: patch.interfaces,\n })\n ) {\n registration.update(patch)\n return\n }\n // Rebuild the remote *before* patching the registry — the patch reloads the\n // page, which must re-fetch a remote that already exposes the new interface.\n const rebuiltServer = await onInterfaceSetChange?.()\n // Commit only after a successful rebuild, so a thrown one retries next pass.\n exposesSet.commit({\n configs: patch.configs,\n interfaces: patch.interfaces,\n })\n // The recreated server can bind a different port (non-strict ports).\n registration.update(rebuiltServer ? {...patch, ...serverAddress(rebuiltServer)} : patch)\n },\n workDir,\n })\n\n return {\n close: async () => {\n registration.release()\n await watcher.close()\n },\n }\n}\n"],"names":["getCliConfigUncached","resolveAppId","deriveInterfaces","formatWorkbenchAppErrors","validateWorkbenchApp","deriveConfigs","trackExposesSet","registerDevServer","startDevManifestWatcher","reportConfigErrors","app","output","errors","length","warn","serverAddress","server","resolvedHost","config","host","addr","httpServer","address","port","startDevServerRegistration","options","cliConfig","extractManifest","isApp","onInterfaceSetChange","workDir","appHost","appPort","interfaces","configs","registration","id","projectId","api","type","exposesSet","watcher","extract","params","manifest","extraWatchFilenames","undefined","update","patch","changed","rebuiltServer","commit","close","release"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/dev/startDevServerRegistration.ts"],"sourcesContent":["import {type CliConfig, getCliConfigUncached, type Output} from '@sanity/cli-core'\nimport {type ViteDevServer} from 'vite'\n\nimport {isWorkbenchApp} from '../../defineApp.js'\nimport {deriveInterfaces} from '../../deriveInterfaces.js'\nimport {formatWorkbenchAppErrors, validateWorkbenchApp} from '../../validateWorkbenchApp.js'\nimport {deriveConfigs} from './deriveConfigs.js'\nimport {trackExposesSet} from './exposesSetId.js'\nimport {type DevServerManifest, getRegisteredServers, registerDevServer} from './registry.js'\nimport {startDevManifestWatcher} from './startDevManifestWatcher.js'\n\ninterface DevServerRegistrationOptions {\n cliConfig: CliConfig\n /**\n * Extract the project manifest to inline into the registry. The caller owns the\n * studio-vs-app split (manifest formats are CLI-domain); registration re-derives\n * the interface set alongside it.\n */\n extractManifest: (params: {\n configPath: string\n workDir: string\n }) => Promise<DevServerManifest['manifest']>\n isApp: boolean\n output: Output\n server: ViteDevServer\n workDir: string\n\n /**\n * Rebuild the app's federation remote when its interface set changes, awaited\n * *before* the registry patch — the patch reloads the workbench page, which must\n * re-fetch a remote that already exposes the new interface. Resolves with the\n * recreated server so the entry gets its actual address (non-strict ports may\n * shift it); must reject if the restart produces no server, so the set stays\n * uncommitted and the next save retries instead of advertising a dead port.\n */\n onInterfaceSetChange?: () => Promise<ViteDevServer>\n}\n\ninterface DevServerRegistrationHandle {\n close: () => Promise<void>\n}\n\n/**\n * Log any config validation errors without aborting. Unlike build and deploy,\n * dev stays up on an invalid config so the author sees the errors and fixes them\n * live on the next save.\n */\nfunction reportConfigErrors(app: CliConfig['app'], output: Output): void {\n const errors = validateWorkbenchApp(app)\n if (errors.length === 0) return\n output.warn(formatWorkbenchAppErrors(errors))\n}\n\n/** The address the server actually bound — the live socket, which can differ from the configured port under non-strict ports. */\nfunction serverAddress(server: ViteDevServer) {\n const resolvedHost = server.config.server.host\n const addr = server.httpServer?.address()\n return {\n host: typeof resolvedHost === 'string' ? resolvedHost : 'localhost',\n port: typeof addr === 'object' && addr ? addr.port : server.config.server.port,\n }\n}\n\n/**\n * Register the dev server in the registry and watch its config for manifest +\n * interface changes. The workbench reads the entry to locate and render the\n * server; the watcher keeps it current as `sanity.cli.ts` is edited.\n */\nexport async function startDevServerRegistration(\n options: DevServerRegistrationOptions,\n): Promise<DevServerRegistrationHandle> {\n const {cliConfig, extractManifest, isApp, onInterfaceSetChange, output, server, workDir} = options\n\n const {host: appHost, port: appPort} = serverAddress(server)\n\n reportConfigErrors(cliConfig.app, output)\n\n // Forwarded alongside (not inside) the manifest so the workbench renders local\n // panels/workers and reads the configs without a deploy.\n const interfaces = deriveInterfaces(cliConfig.app, {isApp})\n const configs = await deriveConfigs(cliConfig.app)\n\n const id = isWorkbenchApp(cliConfig.app) ? cliConfig.app.slug : undefined\n\n const devServer = id ? getRegisteredServers().find((server) => server.id === id) : undefined\n\n if (id && devServer) {\n output.error(\n `The app \"${id}\" is already served by another dev server running on port ${devServer.port}, ` +\n \"so the workbench can't tell them apart and this one stays out of it. \" +\n 'Stop that server, or give this app its own `slug` in sanity.cli.ts.',\n {exit: false},\n )\n return {close: async () => {}}\n }\n\n const registration = registerDevServer({\n configs,\n host: appHost,\n id,\n interfaces,\n port: appPort,\n projectId: cliConfig?.api?.projectId,\n type: isApp ? 'coreApp' : 'studio',\n workDir,\n })\n\n const exposesSet = trackExposesSet({configs, interfaces})\n\n const watcher = await startDevManifestWatcher({\n // Re-derive every pass (don't omit): the registry patch is a shallow merge,\n // so omitting would wipe the registered set.\n extract: async (params) => {\n const app = (await getCliConfigUncached(params.workDir)).app\n reportConfigErrors(app, output)\n return {\n configs: await deriveConfigs(app),\n interfaces: deriveInterfaces(app, {isApp}),\n manifest: await extractManifest(params),\n }\n },\n // A studio's root resolves to `sanity.config.*` but its interfaces live in\n // `sanity.cli.*` — watch that too. Apps already root at `sanity.cli.*`.\n extraWatchFilenames: isApp ? undefined : ['sanity.cli.js', 'sanity.cli.ts'],\n output,\n update: async (patch) => {\n if (\n !exposesSet.changed({\n configs: patch.configs,\n interfaces: patch.interfaces,\n })\n ) {\n registration.update(patch)\n return\n }\n // Rebuild the remote *before* patching the registry — the patch reloads the\n // page, which must re-fetch a remote that already exposes the new interface.\n const rebuiltServer = await onInterfaceSetChange?.()\n // Commit only after a successful rebuild, so a thrown one retries next pass.\n exposesSet.commit({\n configs: patch.configs,\n interfaces: patch.interfaces,\n })\n // The recreated server can bind a different port (non-strict ports).\n registration.update(rebuiltServer ? {...patch, ...serverAddress(rebuiltServer)} : patch)\n },\n workDir,\n })\n\n return {\n close: async () => {\n registration.release()\n await watcher.close()\n },\n }\n}\n"],"names":["getCliConfigUncached","isWorkbenchApp","deriveInterfaces","formatWorkbenchAppErrors","validateWorkbenchApp","deriveConfigs","trackExposesSet","getRegisteredServers","registerDevServer","startDevManifestWatcher","reportConfigErrors","app","output","errors","length","warn","serverAddress","server","resolvedHost","config","host","addr","httpServer","address","port","startDevServerRegistration","options","cliConfig","extractManifest","isApp","onInterfaceSetChange","workDir","appHost","appPort","interfaces","configs","id","slug","undefined","devServer","find","error","exit","close","registration","projectId","api","type","exposesSet","watcher","extract","params","manifest","extraWatchFilenames","update","patch","changed","rebuiltServer","commit","release"],"mappings":"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"}
@@ -52,13 +52,10 @@ import { serveBuiltApplication } from './serveBuiltApplication.js';
52
52
  checkForDeprecatedAppId();
53
53
  const configPath = (await findProjectRoot(workDir)).path;
54
54
  const workbench = resolveWorkbenchApp(cliConfig);
55
- // Read the id the build inlined so start matches it even for a deploy build
56
- // (which carries the API id, not the shape hash); fall back for older builds.
55
+ if (!workbench) throw new Error('`sanity start` was invoked in a non-workbench application');
57
56
  const inlinedId = await readInlinedAppId(outDir);
58
57
  const configs = await deriveConfigs(cliConfig.app);
59
- // `start` serves a build, so it advertises the build's inlined id (matching
60
- // the bundle's `__SANITY_APP_ID__`), not the dev host-port.
61
- const id = workbench ? inlinedId ?? await buildAppId(workbench) : `${remote.host}-${remote.port}`;
58
+ const id = inlinedId ?? await buildAppId(workbench);
62
59
  const registration = registerDevServer({
63
60
  configs,
64
61
  host: remote.host,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/preview/startWorkbenchPreview.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type CliConfig, findProjectRoot, type Output} from '@sanity/cli-core'\n\nimport {buildAppId, SANITY_APP_ID_FILE} from '../../appId.js'\nimport {deriveInterfaces} from '../../deriveInterfaces.js'\nimport {resolveWorkbenchApp} from '../../resolveWorkbenchApp.js'\nimport {createServerLifecycle, toDisplayHost} from '../../util/serverOrchestration.js'\nimport {deriveConfigs} from '../dev/deriveConfigs.js'\nimport {type DevServerManifest, registerDevServer} from '../dev/registry.js'\nimport {startWorkbenchDevServer} from '../dev/startWorkbenchDevServer.js'\nimport {serveBuiltApplication} from './serveBuiltApplication.js'\n\nexport interface StartWorkbenchPreviewOptions {\n /** Directory for the workbench Vite server's dependency cache. */\n cacheDir: string\n /** CLI-domain `app.id`/`deployment.appId` deprecation check, run before registering. */\n checkForDeprecatedAppId: () => void\n cliConfig: CliConfig\n /** Extract the project manifest to inline into the registry (studio-vs-app handled by the CLI). */\n extractManifest: (params: {\n configPath: string\n workDir: string\n }) => Promise<DevServerManifest['manifest']>\n httpHost: string\n httpPort: number\n isApp: boolean\n /** The built `dist` directory to serve as the federation remote. */\n outDir: string\n output: Output\n reactStrictMode: boolean\n workDir: string\n}\n\n/**\n * `sanity start` for a workbench app: serve a production build the way dev serves\n * a live one. The same singleton workbench shell renders it and the same registry\n * advertises it — only the remote differs, static files from the build output\n * instead of a live Vite dev server. There's no config watcher or rebuild: a\n * build is fixed, so nothing re-syncs.\n *\n * A running workbench claims the configured port, so the built remote binds the\n * next one. Without one the remote takes the configured port and announces its\n * own URL.\n */\nexport async function startWorkbenchPreview(\n options: StartWorkbenchPreviewOptions,\n): Promise<{close: () => Promise<void>}> {\n const {\n cacheDir,\n checkForDeprecatedAppId,\n cliConfig,\n extractManifest,\n httpHost,\n httpPort,\n isApp,\n outDir,\n output,\n reactStrictMode,\n workDir,\n } = options\n\n const {close, closers, installSignalHandlers} = createServerLifecycle()\n\n const workbench = await startWorkbenchDevServer({\n cacheDir,\n cliConfig,\n httpHost,\n httpPort,\n mode: 'preview',\n output,\n reactStrictMode,\n workDir,\n })\n closers.push(workbench.close)\n\n const remotePort = workbench.workbenchAvailable ? workbench.workbenchPort + 1 : httpPort\n\n const remote = await serveBuiltApplication({\n cacheDir,\n httpHost,\n httpPort: remotePort,\n outDir,\n workDir,\n }).catch(async (err) => {\n await close()\n throw err\n })\n closers.push(remote.close)\n\n try {\n // Callers provide CLI-only validation and manifest extraction to keep them\n // out of workbench-cli.\n checkForDeprecatedAppId()\n const configPath = (await findProjectRoot(workDir)).path\n const workbench = resolveWorkbenchApp(cliConfig)\n // Read the id the build inlined so start matches it even for a deploy build\n // (which carries the API id, not the shape hash); fall back for older builds.\n const inlinedId = await readInlinedAppId(outDir)\n const configs = await deriveConfigs(cliConfig.app)\n // `start` serves a build, so it advertises the build's inlined id (matching\n // the bundle's `__SANITY_APP_ID__`), not the dev host-port.\n const id = workbench\n ? (inlinedId ?? (await buildAppId(workbench)))\n : `${remote.host}-${remote.port}`\n const registration = registerDevServer({\n configs,\n host: remote.host,\n id,\n interfaces: deriveInterfaces(cliConfig.app, {isApp}),\n manifest: await extractManifest({configPath, workDir}),\n manifestUpdatedAt: new Date().toISOString(),\n port: remote.port,\n projectId: cliConfig?.api?.projectId,\n type: isApp ? 'coreApp' : 'studio',\n workDir,\n })\n closers.push(async () => registration.release())\n } catch (err) {\n await close()\n throw err\n }\n\n if (workbench.workbenchAvailable) {\n const workbenchUrl = `http://${toDisplayHost(workbench.httpHost)}:${workbench.workbenchPort}`\n output.log(\n `Workbench preview server started at ${styleText(['blue', 'underline'], workbenchUrl)} (serving build on port ${remote.port})`,\n )\n } else {\n const remoteUrl = `http://${toDisplayHost(remote.host)}:${remote.port}`\n output.log(`Serving build at ${styleText(['blue', 'underline'], remoteUrl)}`)\n }\n\n installSignalHandlers()\n\n return {close}\n}\n\n/** The id the build inlined into its bundle, or undefined when absent. */\nasync function readInlinedAppId(outDir: string): Promise<string | undefined> {\n try {\n return (await readFile(path.join(outDir, SANITY_APP_ID_FILE), 'utf8')).trim() || undefined\n } catch {\n return undefined\n }\n}\n"],"names":["readFile","path","styleText","findProjectRoot","buildAppId","SANITY_APP_ID_FILE","deriveInterfaces","resolveWorkbenchApp","createServerLifecycle","toDisplayHost","deriveConfigs","registerDevServer","startWorkbenchDevServer","serveBuiltApplication","startWorkbenchPreview","options","cacheDir","checkForDeprecatedAppId","cliConfig","extractManifest","httpHost","httpPort","isApp","outDir","output","reactStrictMode","workDir","close","closers","installSignalHandlers","workbench","mode","push","remotePort","workbenchAvailable","workbenchPort","remote","catch","err","configPath","inlinedId","readInlinedAppId","configs","app","id","host","port","registration","interfaces","manifest","manifestUpdatedAt","Date","toISOString","projectId","api","type","release","workbenchUrl","log","remoteUrl","join","trim","undefined"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AACzC,OAAOC,UAAU,YAAW;AAC5B,SAAQC,SAAS,QAAO,YAAW;AAEnC,SAAwBC,eAAe,QAAoB,mBAAkB;AAE7E,SAAQC,UAAU,EAAEC,kBAAkB,QAAO,iBAAgB;AAC7D,SAAQC,gBAAgB,QAAO,4BAA2B;AAC1D,SAAQC,mBAAmB,QAAO,+BAA8B;AAChE,SAAQC,qBAAqB,EAAEC,aAAa,QAAO,oCAAmC;AACtF,SAAQC,aAAa,QAAO,0BAAyB;AACrD,SAAgCC,iBAAiB,QAAO,qBAAoB;AAC5E,SAAQC,uBAAuB,QAAO,oCAAmC;AACzE,SAAQC,qBAAqB,QAAO,6BAA4B;AAuBhE;;;;;;;;;;CAUC,GACD,OAAO,eAAeC,sBACpBC,OAAqC;IAErC,MAAM,EACJC,QAAQ,EACRC,uBAAuB,EACvBC,SAAS,EACTC,eAAe,EACfC,QAAQ,EACRC,QAAQ,EACRC,KAAK,EACLC,MAAM,EACNC,MAAM,EACNC,eAAe,EACfC,OAAO,EACR,GAAGX;IAEJ,MAAM,EAACY,KAAK,EAAEC,OAAO,EAAEC,qBAAqB,EAAC,GAAGrB;IAEhD,MAAMsB,YAAY,MAAMlB,wBAAwB;QAC9CI;QACAE;QACAE;QACAC;QACAU,MAAM;QACNP;QACAC;QACAC;IACF;IACAE,QAAQI,IAAI,CAACF,UAAUH,KAAK;IAE5B,MAAMM,aAAaH,UAAUI,kBAAkB,GAAGJ,UAAUK,aAAa,GAAG,IAAId;IAEhF,MAAMe,SAAS,MAAMvB,sBAAsB;QACzCG;QACAI;QACAC,UAAUY;QACVV;QACAG;IACF,GAAGW,KAAK,CAAC,OAAOC;QACd,MAAMX;QACN,MAAMW;IACR;IACAV,QAAQI,IAAI,CAACI,OAAOT,KAAK;IAEzB,IAAI;QACF,2EAA2E;QAC3E,wBAAwB;QACxBV;QACA,MAAMsB,aAAa,AAAC,CAAA,MAAMpC,gBAAgBuB,QAAO,EAAGzB,IAAI;QACxD,MAAM6B,YAAYvB,oBAAoBW;QACtC,4EAA4E;QAC5E,8EAA8E;QAC9E,MAAMsB,YAAY,MAAMC,iBAAiBlB;QACzC,MAAMmB,UAAU,MAAMhC,cAAcQ,UAAUyB,GAAG;QACjD,4EAA4E;QAC5E,4DAA4D;QAC5D,MAAMC,KAAKd,YACNU,aAAc,MAAMpC,WAAW0B,aAChC,GAAGM,OAAOS,IAAI,CAAC,CAAC,EAAET,OAAOU,IAAI,EAAE;QACnC,MAAMC,eAAepC,kBAAkB;YACrC+B;YACAG,MAAMT,OAAOS,IAAI;YACjBD;YACAI,YAAY1C,iBAAiBY,UAAUyB,GAAG,EAAE;gBAACrB;YAAK;YAClD2B,UAAU,MAAM9B,gBAAgB;gBAACoB;gBAAYb;YAAO;YACpDwB,mBAAmB,IAAIC,OAAOC,WAAW;YACzCN,MAAMV,OAAOU,IAAI;YACjBO,WAAWnC,WAAWoC,KAAKD;YAC3BE,MAAMjC,QAAQ,YAAY;YAC1BI;QACF;QACAE,QAAQI,IAAI,CAAC,UAAYe,aAAaS,OAAO;IAC/C,EAAE,OAAOlB,KAAK;QACZ,MAAMX;QACN,MAAMW;IACR;IAEA,IAAIR,UAAUI,kBAAkB,EAAE;QAChC,MAAMuB,eAAe,CAAC,OAAO,EAAEhD,cAAcqB,UAAUV,QAAQ,EAAE,CAAC,EAAEU,UAAUK,aAAa,EAAE;QAC7FX,OAAOkC,GAAG,CACR,CAAC,oCAAoC,EAAExD,UAAU;YAAC;YAAQ;SAAY,EAAEuD,cAAc,wBAAwB,EAAErB,OAAOU,IAAI,CAAC,CAAC,CAAC;IAElI,OAAO;QACL,MAAMa,YAAY,CAAC,OAAO,EAAElD,cAAc2B,OAAOS,IAAI,EAAE,CAAC,EAAET,OAAOU,IAAI,EAAE;QACvEtB,OAAOkC,GAAG,CAAC,CAAC,iBAAiB,EAAExD,UAAU;YAAC;YAAQ;SAAY,EAAEyD,YAAY;IAC9E;IAEA9B;IAEA,OAAO;QAACF;IAAK;AACf;AAEA,wEAAwE,GACxE,eAAec,iBAAiBlB,MAAc;IAC5C,IAAI;QACF,OAAO,AAAC,CAAA,MAAMvB,SAASC,KAAK2D,IAAI,CAACrC,QAAQlB,qBAAqB,OAAM,EAAGwD,IAAI,MAAMC;IACnF,EAAE,OAAM;QACN,OAAOA;IACT;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/preview/startWorkbenchPreview.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type CliConfig, findProjectRoot, type Output} from '@sanity/cli-core'\n\nimport {buildAppId, SANITY_APP_ID_FILE} from '../../appId.js'\nimport {deriveInterfaces} from '../../deriveInterfaces.js'\nimport {resolveWorkbenchApp} from '../../resolveWorkbenchApp.js'\nimport {createServerLifecycle, toDisplayHost} from '../../util/serverOrchestration.js'\nimport {deriveConfigs} from '../dev/deriveConfigs.js'\nimport {type DevServerManifest, registerDevServer} from '../dev/registry.js'\nimport {startWorkbenchDevServer} from '../dev/startWorkbenchDevServer.js'\nimport {serveBuiltApplication} from './serveBuiltApplication.js'\n\nexport interface StartWorkbenchPreviewOptions {\n /** Directory for the workbench Vite server's dependency cache. */\n cacheDir: string\n /** CLI-domain `app.id`/`deployment.appId` deprecation check, run before registering. */\n checkForDeprecatedAppId: () => void\n cliConfig: CliConfig\n /** Extract the project manifest to inline into the registry (studio-vs-app handled by the CLI). */\n extractManifest: (params: {\n configPath: string\n workDir: string\n }) => Promise<DevServerManifest['manifest']>\n httpHost: string\n httpPort: number\n isApp: boolean\n /** The built `dist` directory to serve as the federation remote. */\n outDir: string\n output: Output\n reactStrictMode: boolean\n workDir: string\n}\n\n/**\n * `sanity start` for a workbench app: serve a production build the way dev serves\n * a live one. The same singleton workbench shell renders it and the same registry\n * advertises it — only the remote differs, static files from the build output\n * instead of a live Vite dev server. There's no config watcher or rebuild: a\n * build is fixed, so nothing re-syncs.\n *\n * A running workbench claims the configured port, so the built remote binds the\n * next one. Without one the remote takes the configured port and announces its\n * own URL.\n */\nexport async function startWorkbenchPreview(\n options: StartWorkbenchPreviewOptions,\n): Promise<{close: () => Promise<void>}> {\n const {\n cacheDir,\n checkForDeprecatedAppId,\n cliConfig,\n extractManifest,\n httpHost,\n httpPort,\n isApp,\n outDir,\n output,\n reactStrictMode,\n workDir,\n } = options\n\n const {close, closers, installSignalHandlers} = createServerLifecycle()\n\n const workbench = await startWorkbenchDevServer({\n cacheDir,\n cliConfig,\n httpHost,\n httpPort,\n mode: 'preview',\n output,\n reactStrictMode,\n workDir,\n })\n closers.push(workbench.close)\n\n const remotePort = workbench.workbenchAvailable ? workbench.workbenchPort + 1 : httpPort\n\n const remote = await serveBuiltApplication({\n cacheDir,\n httpHost,\n httpPort: remotePort,\n outDir,\n workDir,\n }).catch(async (err) => {\n await close()\n throw err\n })\n closers.push(remote.close)\n\n try {\n // Callers provide CLI-only validation and manifest extraction to keep them\n // out of workbench-cli.\n checkForDeprecatedAppId()\n const configPath = (await findProjectRoot(workDir)).path\n const workbench = resolveWorkbenchApp(cliConfig)\n\n if (!workbench) throw new Error('`sanity start` was invoked in a non-workbench application')\n const inlinedId = await readInlinedAppId(outDir)\n const configs = await deriveConfigs(cliConfig.app)\n const id = inlinedId ?? (await buildAppId(workbench))\n const registration = registerDevServer({\n configs,\n host: remote.host,\n id,\n interfaces: deriveInterfaces(cliConfig.app, {isApp}),\n manifest: await extractManifest({configPath, workDir}),\n manifestUpdatedAt: new Date().toISOString(),\n port: remote.port,\n projectId: cliConfig?.api?.projectId,\n type: isApp ? 'coreApp' : 'studio',\n workDir,\n })\n closers.push(async () => registration.release())\n } catch (err) {\n await close()\n throw err\n }\n\n if (workbench.workbenchAvailable) {\n const workbenchUrl = `http://${toDisplayHost(workbench.httpHost)}:${workbench.workbenchPort}`\n output.log(\n `Workbench preview server started at ${styleText(['blue', 'underline'], workbenchUrl)} (serving build on port ${remote.port})`,\n )\n } else {\n const remoteUrl = `http://${toDisplayHost(remote.host)}:${remote.port}`\n output.log(`Serving build at ${styleText(['blue', 'underline'], remoteUrl)}`)\n }\n\n installSignalHandlers()\n\n return {close}\n}\n\n/** The id the build inlined into its bundle, or undefined when absent. */\nasync function readInlinedAppId(outDir: string): Promise<string | undefined> {\n try {\n return (await readFile(path.join(outDir, SANITY_APP_ID_FILE), 'utf8')).trim() || undefined\n } catch {\n return undefined\n }\n}\n"],"names":["readFile","path","styleText","findProjectRoot","buildAppId","SANITY_APP_ID_FILE","deriveInterfaces","resolveWorkbenchApp","createServerLifecycle","toDisplayHost","deriveConfigs","registerDevServer","startWorkbenchDevServer","serveBuiltApplication","startWorkbenchPreview","options","cacheDir","checkForDeprecatedAppId","cliConfig","extractManifest","httpHost","httpPort","isApp","outDir","output","reactStrictMode","workDir","close","closers","installSignalHandlers","workbench","mode","push","remotePort","workbenchAvailable","workbenchPort","remote","catch","err","configPath","Error","inlinedId","readInlinedAppId","configs","app","id","registration","host","interfaces","manifest","manifestUpdatedAt","Date","toISOString","port","projectId","api","type","release","workbenchUrl","log","remoteUrl","join","trim","undefined"],"mappings":"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"}
@@ -58,17 +58,19 @@ async function resolveApplicationTarget({ appId, type, workbench }) {
58
58
  type: 'none'
59
59
  };
60
60
  }
61
- const { exposes, lines } = summarizeInterfaces(workbench);
61
+ const { lines, services, views } = summarizeInterfaces(workbench);
62
62
  return {
63
63
  target: {
64
- activeDeployment: null,
65
- appHost: application.slug,
66
- createdAt: null,
64
+ application,
67
65
  deletes: 'application',
68
66
  id: application.id,
69
- interfaces: exposes,
70
- organizationId: application.organizationId,
71
- projectId: null,
67
+ payload: {
68
+ appId: application.id,
69
+ services,
70
+ type,
71
+ views
72
+ },
73
+ services,
72
74
  summary: [
73
75
  ...lines,
74
76
  ...workbench.isSingleton === undefined ? [] : [
@@ -80,7 +82,8 @@ async function resolveApplicationTarget({ appId, type, workbench }) {
80
82
  url: getApplicationUrl({
81
83
  ...application,
82
84
  type
83
- })
85
+ }),
86
+ views
84
87
  },
85
88
  type: 'found'
86
89
  };
@@ -114,30 +117,23 @@ async function resolveConfigTarget({ organizationId, workbench }) {
114
117
  }
115
118
  };
116
119
  }
117
- // At most one snapshot is active (served); the rest are deactivated history.
118
- const active = configs.find((snapshot)=>snapshot.isActive);
119
120
  return {
120
121
  installationId,
121
122
  resolution: {
122
123
  target: {
123
- activeDeployment: active ? {
124
- deployedAt: active.createdAt ?? '',
125
- deployedBy: active.deployedBy ?? ''
126
- } : null,
127
- appHost: null,
128
- configs: configs.map((snapshot)=>({
129
- createdAt: snapshot.createdAt ?? null,
130
- deployedBy: snapshot.deployedBy ?? null,
131
- id: snapshot.id
132
- })),
133
- createdAt: configs.at(-1)?.createdAt ?? null,
124
+ configs,
134
125
  deletes: 'config',
135
126
  id: null,
136
- organizationId,
137
- projectId: null,
127
+ payload: {
128
+ appId: null,
129
+ ...config ? {
130
+ config: summarizeConfig(config)
131
+ } : {},
132
+ type: 'coreApp'
133
+ },
138
134
  summary: config ? [
139
135
  summarizeConfig(config)
140
- ] : undefined,
136
+ ] : [],
141
137
  title: workbench.slug,
142
138
  type: 'coreApp',
143
139
  url: getWorkbenchUrl(organizationId)
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/undeploy/workbenchUndeployAdapter.ts"],"sourcesContent":["import {\n type UndeployAdapter,\n type UndeployApplicationTarget,\n type UndeployConfigTarget,\n type UndeployTargetResolution,\n} from '@sanity/cli-core/undeploy'\n\nimport {\n deleteApplication,\n getApplication,\n getApplicationUrl,\n getWorkbenchUrl,\n} from '../../services/applications.js'\nimport {deleteConfig, listConfigs} from '../../services/installations.js'\nimport {resolveInstallationId, summarizeConfig} from '../deploy/deployConfig.js'\nimport {type DeployableWorkbenchApp} from '../deploy/getWorkbench.js'\nimport {type DeployedExpose, summarizeInterfaces} from '../deploy/summarizeInterfaces.js'\n\n/** The workbench extension of the shared target; serializes into `--json` as-is. */\nexport type WorkbenchUndeployTarget =\n | (UndeployApplicationTarget & {\n /** Interfaces (views and services) registered by the application. */\n interfaces: DeployedExpose[]\n })\n | (UndeployConfigTarget & {\n /** The deployed config snapshots an undeploy deletes. */\n configs: {\n createdAt: string | null\n deployedBy: string | null\n id: string\n }[]\n })\n\n/**\n * The undeploy adapter for workbench apps, mirroring what a workbench deploy\n * creates: apps that expose interfaces delete their Brett application (the\n * server soft-deletes its deployments and refuses singletons with active\n * installations); a singleton without interfaces — the media library — deletes\n * its installation's config snapshots instead.\n */\nexport function createWorkbenchUndeployAdapter(options: {\n appId: string | undefined\n organizationId: string | undefined\n type: 'coreApp' | 'studio'\n workbench: DeployableWorkbenchApp\n}): UndeployAdapter<WorkbenchUndeployTarget> {\n const {appId, organizationId, type, workbench} = options\n // Keyed on singleton-ness, not on a locally declared config, so an undeploy\n // still reaches the server's config snapshots after the fields are removed\n // from sanity.cli.ts.\n const configOnly = !!workbench.isSingleton && !workbench.hasInterfaces\n // Workbench-internal, so kept off the reported target; resolveTarget stashes it for the delete.\n let installationId: string | undefined\n\n return {\n resolveTarget: async () => {\n if (!configOnly) return resolveApplicationTarget({appId, type, workbench})\n const resolved = await resolveConfigTarget({organizationId, workbench})\n installationId = resolved.installationId\n return resolved.resolution\n },\n type,\n async undeploy(target) {\n if (target.deletes === 'config') {\n if (!installationId) throw new Error('No installation resolved for the config undeploy')\n for (const snapshot of target.configs) {\n await deleteConfig(installationId, snapshot.id)\n }\n return\n }\n await deleteApplication(target.id)\n },\n }\n}\n\nasync function resolveApplicationTarget({\n appId,\n type,\n workbench,\n}: {\n appId: string | undefined\n type: 'coreApp' | 'studio'\n workbench: DeployableWorkbenchApp\n}): Promise<UndeployTargetResolution<WorkbenchUndeployTarget>> {\n if (!appId) {\n return {\n message: 'No `deployment.appId` configured',\n solution: 'Add `deployment.appId` to sanity.cli.ts',\n type: 'none',\n }\n }\n\n const application = await getApplication(appId)\n if (!application) {\n return {message: 'Application with the given ID does not exist', type: 'none'}\n }\n\n const {exposes, lines} = summarizeInterfaces(workbench)\n return {\n target: {\n activeDeployment: null,\n appHost: application.slug,\n createdAt: null,\n deletes: 'application',\n id: application.id,\n interfaces: exposes,\n organizationId: application.organizationId,\n projectId: null,\n summary: [\n ...lines,\n ...(workbench.isSingleton === undefined ? [] : [`Singleton: ${workbench.isSingleton}`]),\n ],\n title: application.title,\n type,\n url: getApplicationUrl({...application, type}),\n },\n type: 'found',\n }\n}\n\nasync function resolveConfigTarget({\n organizationId,\n workbench,\n}: {\n organizationId: string | undefined\n workbench: DeployableWorkbenchApp\n}): Promise<{\n installationId?: string\n resolution: UndeployTargetResolution<WorkbenchUndeployTarget>\n}> {\n const config = workbench.config\n const appType = config?.appType ?? workbench.applicationType\n if (!appType) throw new Error('The app declares no app type to resolve its installation')\n if (!organizationId) {\n throw new Error(\n 'sanity.cli.ts does not contain an organization identifier (\"app.organizationId\"), which is required to resolve the installation',\n )\n }\n\n const installationId = await resolveInstallationId({appType, organizationId})\n if (!installationId) {\n return {\n resolution: {\n message: `No active \"${appType}\" installation for organization \"${organizationId}\"`,\n type: 'none',\n },\n }\n }\n\n const configs = await listConfigs(installationId)\n if (configs.length === 0) {\n return {\n installationId,\n resolution: {\n message: `No deployed config for the \"${appType}\" installation`,\n type: 'none',\n },\n }\n }\n\n // At most one snapshot is active (served); the rest are deactivated history.\n const active = configs.find((snapshot) => snapshot.isActive)\n return {\n installationId,\n resolution: {\n target: {\n activeDeployment: active\n ? {deployedAt: active.createdAt ?? '', deployedBy: active.deployedBy ?? ''}\n : null,\n appHost: null,\n configs: configs.map((snapshot) => ({\n createdAt: snapshot.createdAt ?? null,\n deployedBy: snapshot.deployedBy ?? null,\n id: snapshot.id,\n })),\n createdAt: configs.at(-1)?.createdAt ?? null,\n deletes: 'config',\n id: null,\n organizationId,\n projectId: null,\n summary: config ? [summarizeConfig(config)] : undefined,\n title: workbench.slug,\n type: 'coreApp',\n url: getWorkbenchUrl(organizationId),\n },\n type: 'found',\n },\n }\n}\n"],"names":["deleteApplication","getApplication","getApplicationUrl","getWorkbenchUrl","deleteConfig","listConfigs","resolveInstallationId","summarizeConfig","summarizeInterfaces","createWorkbenchUndeployAdapter","options","appId","organizationId","type","workbench","configOnly","isSingleton","hasInterfaces","installationId","resolveTarget","resolveApplicationTarget","resolved","resolveConfigTarget","resolution","undeploy","target","deletes","Error","snapshot","configs","id","message","solution","application","exposes","lines","activeDeployment","appHost","slug","createdAt","interfaces","projectId","summary","undefined","title","url","config","appType","applicationType","length","active","find","isActive","deployedAt","deployedBy","map","at"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/undeploy/workbenchUndeployAdapter.ts"],"sourcesContent":["import {\n type UndeployAdapter,\n type UndeployApplicationTarget,\n type UndeployConfigTarget,\n type UndeployTargetResolution,\n} from '@sanity/cli-core/undeploy'\n\nimport {\n deleteApplication,\n getApplication,\n getApplicationUrl,\n getWorkbenchUrl,\n} from '../../services/applications.js'\nimport {type ConfigSnapshot, deleteConfig, listConfigs} from '../../services/installations.js'\nimport {resolveInstallationId, summarizeConfig} from '../deploy/deployConfig.js'\nimport {type DeployableWorkbenchApp} from '../deploy/getWorkbench.js'\nimport {type DeployedInterface, summarizeInterfaces} from '../deploy/summarizeInterfaces.js'\n\n/** The workbench extension of the shared target; serializes into `--json` as-is. */\nexport type WorkbenchUndeployTarget =\n | (UndeployApplicationTarget & {\n services: DeployedInterface[]\n views: DeployedInterface[]\n })\n | (UndeployConfigTarget & {\n configs: ConfigSnapshot[]\n })\n\n/**\n * The undeploy adapter for workbench apps, mirroring what a workbench deploy\n * creates: apps that expose interfaces delete their Brett application (the\n * server soft-deletes its deployments and refuses singletons with active\n * installations); a singleton without interfaces — the media library — deletes\n * its installation's config snapshots instead.\n */\nexport function createWorkbenchUndeployAdapter(options: {\n appId: string | undefined\n organizationId: string | undefined\n type: 'coreApp' | 'studio'\n workbench: DeployableWorkbenchApp\n}): UndeployAdapter<WorkbenchUndeployTarget> {\n const {appId, organizationId, type, workbench} = options\n // Keyed on singleton-ness, not on a locally declared config, so an undeploy\n // still reaches the server's config snapshots after the fields are removed\n // from sanity.cli.ts.\n const configOnly = !!workbench.isSingleton && !workbench.hasInterfaces\n // Workbench-internal, so kept off the reported target; resolveTarget stashes it for the delete.\n let installationId: string | undefined\n\n return {\n resolveTarget: async () => {\n if (!configOnly) return resolveApplicationTarget({appId, type, workbench})\n const resolved = await resolveConfigTarget({organizationId, workbench})\n installationId = resolved.installationId\n return resolved.resolution\n },\n type,\n async undeploy(target) {\n if (target.deletes === 'config') {\n if (!installationId) throw new Error('No installation resolved for the config undeploy')\n for (const snapshot of target.configs) {\n await deleteConfig(installationId, snapshot.id)\n }\n return\n }\n await deleteApplication(target.id)\n },\n }\n}\n\nasync function resolveApplicationTarget({\n appId,\n type,\n workbench,\n}: {\n appId: string | undefined\n type: 'coreApp' | 'studio'\n workbench: DeployableWorkbenchApp\n}): Promise<UndeployTargetResolution<WorkbenchUndeployTarget>> {\n if (!appId) {\n return {\n message: 'No `deployment.appId` configured',\n solution: 'Add `deployment.appId` to sanity.cli.ts',\n type: 'none',\n }\n }\n\n const application = await getApplication(appId)\n if (!application) {\n return {message: 'Application with the given ID does not exist', type: 'none'}\n }\n\n const {lines, services, views} = summarizeInterfaces(workbench)\n return {\n target: {\n application,\n deletes: 'application',\n id: application.id,\n payload: {appId: application.id, services, type, views},\n services,\n summary: [\n ...lines,\n ...(workbench.isSingleton === undefined ? [] : [`Singleton: ${workbench.isSingleton}`]),\n ],\n title: application.title,\n type,\n url: getApplicationUrl({...application, type}),\n views,\n },\n type: 'found',\n }\n}\n\nasync function resolveConfigTarget({\n organizationId,\n workbench,\n}: {\n organizationId: string | undefined\n workbench: DeployableWorkbenchApp\n}): Promise<{\n installationId?: string\n resolution: UndeployTargetResolution<WorkbenchUndeployTarget>\n}> {\n const config = workbench.config\n const appType = config?.appType ?? workbench.applicationType\n if (!appType) throw new Error('The app declares no app type to resolve its installation')\n if (!organizationId) {\n throw new Error(\n 'sanity.cli.ts does not contain an organization identifier (\"app.organizationId\"), which is required to resolve the installation',\n )\n }\n\n const installationId = await resolveInstallationId({appType, organizationId})\n if (!installationId) {\n return {\n resolution: {\n message: `No active \"${appType}\" installation for organization \"${organizationId}\"`,\n type: 'none',\n },\n }\n }\n\n const configs = await listConfigs(installationId)\n if (configs.length === 0) {\n return {\n installationId,\n resolution: {\n message: `No deployed config for the \"${appType}\" installation`,\n type: 'none',\n },\n }\n }\n\n return {\n installationId,\n resolution: {\n target: {\n configs,\n deletes: 'config',\n id: null,\n payload: {\n appId: null,\n ...(config ? {config: summarizeConfig(config)} : {}),\n type: 'coreApp',\n },\n summary: config ? [summarizeConfig(config)] : [],\n title: workbench.slug,\n type: 'coreApp',\n url: getWorkbenchUrl(organizationId),\n },\n type: 'found',\n },\n }\n}\n"],"names":["deleteApplication","getApplication","getApplicationUrl","getWorkbenchUrl","deleteConfig","listConfigs","resolveInstallationId","summarizeConfig","summarizeInterfaces","createWorkbenchUndeployAdapter","options","appId","organizationId","type","workbench","configOnly","isSingleton","hasInterfaces","installationId","resolveTarget","resolveApplicationTarget","resolved","resolveConfigTarget","resolution","undeploy","target","deletes","Error","snapshot","configs","id","message","solution","application","lines","services","views","payload","summary","undefined","title","url","config","appType","applicationType","length","slug"],"mappings":"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"}
package/dist/appId.js CHANGED
@@ -3,20 +3,11 @@
3
3
  * bundle. `sanity start` serves a build without recompiling, so it reads this
4
4
  * instead of recomputing — a deploy inlines the API id, not the shape hash.
5
5
  */ export const SANITY_APP_ID_FILE = 'sanity-app-id.txt';
6
- /**
7
- * The dev id for a workbench app — the address the server bound. `sanity dev`
8
- * keys on where the app is served so a running app can't collide with its
9
- * deployed twin. Sync and dependency-free: it's re-exported from the package's
10
- * browser-facing entry, so it must not pull in `node:crypto`.
11
- */ export function resolveAppId(source) {
12
- return `${source.host}-${source.port}`;
13
- }
14
6
  /**
15
7
  * The `build`/`start` id — a hash of the app's declared shape (its identity, not
16
8
  * its code), so the bundle inlined by `sanity build` and the registry entry
17
- * advertised by `sanity start` resolve to the same id. Hashed with the Web Crypto
18
- * API rather than `node:crypto` for parity with `resolveAppId`'s browser-safe
19
- * home. `sanity deploy` resolves its own id from the applications API.
9
+ * advertised by `sanity start` resolve to the same id. `sanity deploy` resolves
10
+ * its own id from the applications API.
20
11
  */ export async function buildAppId(app) {
21
12
  const canonical = (interfaces)=>(interfaces ?? []).map((i)=>[
22
13
  i.type,
package/dist/appId.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/appId.ts"],"sourcesContent":["import {type ResolvedWorkbenchApp} from './resolveWorkbenchApp.js'\n\n/**\n * File the build writes into its output, carrying the id compiled into the\n * bundle. `sanity start` serves a build without recompiling, so it reads this\n * instead of recomputing — a deploy inlines the API id, not the shape hash.\n */\nexport const SANITY_APP_ID_FILE = 'sanity-app-id.txt'\n\n/**\n * The dev id for a workbench app — the address the server bound. `sanity dev`\n * keys on where the app is served so a running app can't collide with its\n * deployed twin. Sync and dependency-free: it's re-exported from the package's\n * browser-facing entry, so it must not pull in `node:crypto`.\n */\nexport function resolveAppId(source: {host: string; port: number}): string {\n return `${source.host}-${source.port}`\n}\n\n/**\n * The `build`/`start` id — a hash of the app's declared shape (its identity, not\n * its code), so the bundle inlined by `sanity build` and the registry entry\n * advertised by `sanity start` resolve to the same id. Hashed with the Web Crypto\n * API rather than `node:crypto` for parity with `resolveAppId`'s browser-safe\n * home. `sanity deploy` resolves its own id from the applications API.\n */\nexport async function buildAppId(app: ResolvedWorkbenchApp): Promise<string> {\n const canonical = (\n interfaces: ReadonlyArray<{name: string; src: string; type: string}> | undefined,\n ): Array<[string, string, string]> =>\n (interfaces ?? []).map((i): [string, string, string] => [i.type, i.name, i.src]).toSorted()\n const shape = JSON.stringify({\n config: app.config ?? null,\n entry: app.entry ?? null,\n organizationId: app.organizationId,\n services: canonical(app.services),\n slug: app.slug,\n views: canonical(app.views),\n })\n // eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(shape))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n"],"names":["SANITY_APP_ID_FILE","resolveAppId","source","host","port","buildAppId","app","canonical","interfaces","map","i","type","name","src","toSorted","shape","JSON","stringify","config","entry","organizationId","services","slug","views","digest","globalThis","crypto","subtle","TextEncoder","encode","Array","from","Uint8Array","byte","toString","padStart","join"],"mappings":"AAEA;;;;CAIC,GACD,OAAO,MAAMA,qBAAqB,oBAAmB;AAErD;;;;;CAKC,GACD,OAAO,SAASC,aAAaC,MAAoC;IAC/D,OAAO,GAAGA,OAAOC,IAAI,CAAC,CAAC,EAAED,OAAOE,IAAI,EAAE;AACxC;AAEA;;;;;;CAMC,GACD,OAAO,eAAeC,WAAWC,GAAyB;IACxD,MAAMC,YAAY,CAChBC,aAEA,AAACA,CAAAA,cAAc,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC,IAAgC;gBAACA,EAAEC,IAAI;gBAAED,EAAEE,IAAI;gBAAEF,EAAEG,GAAG;aAAC,EAAEC,QAAQ;IAC3F,MAAMC,QAAQC,KAAKC,SAAS,CAAC;QAC3BC,QAAQZ,IAAIY,MAAM,IAAI;QACtBC,OAAOb,IAAIa,KAAK,IAAI;QACpBC,gBAAgBd,IAAIc,cAAc;QAClCC,UAAUd,UAAUD,IAAIe,QAAQ;QAChCC,MAAMhB,IAAIgB,IAAI;QACdC,OAAOhB,UAAUD,IAAIiB,KAAK;IAC5B;IACA,+IAA+I;IAC/I,MAAMC,SAAS,MAAMC,WAAWC,MAAM,CAACC,MAAM,CAACH,MAAM,CAAC,WAAW,IAAII,cAAcC,MAAM,CAACd;IACzF,OAAOe,MAAMC,IAAI,CAAC,IAAIC,WAAWR,SAAS,CAACS,OAASA,KAAKC,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAG,MAAMC,IAAI,CAAC;AAC/F"}
1
+ {"version":3,"sources":["../src/appId.ts"],"sourcesContent":["import {type ResolvedWorkbenchApp} from './resolveWorkbenchApp.js'\n\n/**\n * File the build writes into its output, carrying the id compiled into the\n * bundle. `sanity start` serves a build without recompiling, so it reads this\n * instead of recomputing — a deploy inlines the API id, not the shape hash.\n */\nexport const SANITY_APP_ID_FILE = 'sanity-app-id.txt'\n\n/**\n * The `build`/`start` id — a hash of the app's declared shape (its identity, not\n * its code), so the bundle inlined by `sanity build` and the registry entry\n * advertised by `sanity start` resolve to the same id. `sanity deploy` resolves\n * its own id from the applications API.\n */\nexport async function buildAppId(app: ResolvedWorkbenchApp): Promise<string> {\n const canonical = (\n interfaces: ReadonlyArray<{name: string; src: string; type: string}> | undefined,\n ): Array<[string, string, string]> =>\n (interfaces ?? []).map((i): [string, string, string] => [i.type, i.name, i.src]).toSorted()\n const shape = JSON.stringify({\n config: app.config ?? null,\n entry: app.entry ?? null,\n organizationId: app.organizationId,\n services: canonical(app.services),\n slug: app.slug,\n views: canonical(app.views),\n })\n // eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(shape))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n"],"names":["SANITY_APP_ID_FILE","buildAppId","app","canonical","interfaces","map","i","type","name","src","toSorted","shape","JSON","stringify","config","entry","organizationId","services","slug","views","digest","globalThis","crypto","subtle","TextEncoder","encode","Array","from","Uint8Array","byte","toString","padStart","join"],"mappings":"AAEA;;;;CAIC,GACD,OAAO,MAAMA,qBAAqB,oBAAmB;AAErD;;;;;CAKC,GACD,OAAO,eAAeC,WAAWC,GAAyB;IACxD,MAAMC,YAAY,CAChBC,aAEA,AAACA,CAAAA,cAAc,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC,IAAgC;gBAACA,EAAEC,IAAI;gBAAED,EAAEE,IAAI;gBAAEF,EAAEG,GAAG;aAAC,EAAEC,QAAQ;IAC3F,MAAMC,QAAQC,KAAKC,SAAS,CAAC;QAC3BC,QAAQZ,IAAIY,MAAM,IAAI;QACtBC,OAAOb,IAAIa,KAAK,IAAI;QACpBC,gBAAgBd,IAAIc,cAAc;QAClCC,UAAUd,UAAUD,IAAIe,QAAQ;QAChCC,MAAMhB,IAAIgB,IAAI;QACdC,OAAOhB,UAAUD,IAAIiB,KAAK;IAC5B;IACA,+IAA+I;IAC/I,MAAMC,SAAS,MAAMC,WAAWC,MAAM,CAACC,MAAM,CAACH,MAAM,CAAC,WAAW,IAAII,cAAcC,MAAM,CAACd;IACzF,OAAOe,MAAMC,IAAI,CAAC,IAAIC,WAAWR,SAAS,CAACS,OAASA,KAAKC,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAG,MAAMC,IAAI,CAAC;AAC/F"}
@@ -79,10 +79,11 @@ export async function updateApplication(applicationId, update) {
79
79
  });
80
80
  }
81
81
  /** Deploy a new active version to an existing application. */ export async function createDeployment(options) {
82
- const { applicationId, interfaces, isAutoUpdating, tarball, version, workspaces } = options;
82
+ const { access, applicationId, interfaces, isAutoUpdating, tarball, version, workspaces } = options;
83
83
  const formData = new FormData();
84
84
  formData.append('isAutoUpdating', isAutoUpdating.toString());
85
85
  appendDeploymentParts(formData, {
86
+ access,
86
87
  interfaces,
87
88
  tarball,
88
89
  version,
@@ -101,11 +102,13 @@ export async function updateApplication(applicationId, update) {
101
102
  if (err?.statusCode !== 404) throw err;
102
103
  }
103
104
  }
104
- function appendDeploymentParts(formData, { interfaces, tarball, version, workspaces }) {
105
+ function appendDeploymentParts(formData, { access, interfaces, tarball, version, workspaces }) {
105
106
  formData.append('version', version);
106
107
  appendJson(formData, 'interfaces', interfaces);
107
108
  // Studio-only — the server rejects a workspaces part on non-studio types.
108
109
  if (workspaces?.length) appendJson(formData, 'workspaces', workspaces);
110
+ // Per-deployment; forbidden for singletons, so callers omit it there.
111
+ if (access?.length) appendJson(formData, 'access', access);
109
112
  formData.append('tarball', tarball, {
110
113
  contentType: 'application/gzip',
111
114
  filename: 'app.tar.gz'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/applications.ts"],"sourcesContent":["import {PassThrough} from 'node:stream'\nimport {type Gzip} from 'node:zlib'\n\nimport {type AppVisibility, getGlobalCliClient} from '@sanity/cli-core'\nimport {isStaging} from '@sanity/cli-core/util'\nimport FormData from 'form-data'\n\nimport {type AppInterfaceMetadata, type TileInterfaceMetadata} from '../contract.js'\nimport {APP_WORKBENCH_API_VERSION} from './apiVersion.js'\n\nexport type ApplicationType = 'coreApp' | 'studio'\n\nexport interface Application {\n id: string\n organizationId: string\n slug: string | null\n title: string\n type: ApplicationType\n}\n\ninterface BrettInterfaceBase {\n moduleId: string\n name: string\n title: string\n version: string\n}\n\n/**\n * An interface as Brett stores it, discriminated on `type`. `moduleId` is\n * remote-relative — the host prepends the app's id. Brett assigns the id.\n * @internal\n */\nexport type BrettInterface =\n | (BrettInterfaceBase & {metadata: AppInterfaceMetadata | null; type: 'app'})\n | (BrettInterfaceBase & {metadata: null; type: 'asset_source'})\n | (BrettInterfaceBase & {metadata: null; type: 'panel'})\n | (BrettInterfaceBase & {metadata: null; type: 'worker'})\n | (BrettInterfaceBase & {metadata: TileInterfaceMetadata; type: 'tile'})\n\n/** A studio workspace as Brett stores it. */\nexport interface BrettWorkspace {\n dataset: string\n projectId: string\n /** Lexicon schema descriptor id; Brett requires one per workspace. */\n schemaDescriptorId: string\n\n basePath?: string\n icon?: string\n name?: string\n subtitle?: string\n title?: string\n}\n\nexport function getWorkbenchUrl(organizationId: string): string {\n return `https://${organizationId}.${isStaging() ? 'run.sanity.work' : 'sanity.run'}`\n}\n\n/** Where a deployed application is served on its organization's workbench. */\nexport function getApplicationUrl(\n application: Pick<Application, 'id' | 'organizationId' | 'type'>,\n): string {\n const segment = application.type === 'studio' ? 'studio' : 'application'\n return `${getWorkbenchUrl(application.organizationId)}/${segment}/${application.id}`\n}\n\nasync function getClient() {\n return getGlobalCliClient({apiVersion: APP_WORKBENCH_API_VERSION, requireUser: true})\n}\n\nexport async function getApplication(applicationId: string): Promise<Application | null> {\n const client = await getClient()\n try {\n return await client.request({uri: `/applications/${applicationId}`})\n } catch (err) {\n if ((err as {statusCode?: number})?.statusCode === 404) return null\n throw err\n }\n}\n\n/** Every application in an organization, in one page (`limit=none`). */\nexport async function listApplications(organizationId: string): Promise<Application[]> {\n const client = await getClient()\n const {data}: {data: Application[]} = await client.request({\n query: {limit: 'none', organizationId},\n uri: '/applications',\n })\n return data\n}\n\n/**\n * Create an application record (no deployment), so the CLI can build with the\n * returned id, then ship it via {@link createDeployment}.\n */\nexport async function createApplication(options: {\n isSingleton?: boolean\n organizationId: string\n projectId?: string\n slug: string\n title: string\n type: ApplicationType\n visibility?: AppVisibility\n}): Promise<Application> {\n const {isSingleton, organizationId, projectId, slug, title, type, visibility} = options\n const client = await getClient()\n return client.request({\n body: {\n organizationId,\n slug,\n title,\n type,\n ...(isSingleton === undefined ? {} : {isSingleton}),\n ...(visibility ? {visibility} : {}),\n // Studio config is set once, at create — it's immutable on redeploy.\n ...(projectId ? {config: {studio: {projectId}}} : {}),\n },\n method: 'POST',\n uri: `/applications`,\n })\n}\n\n/** Mutable application fields the deploy flow patches after create. */\nexport interface ApplicationUpdate {\n icon?: string | null\n title?: string\n visibility?: AppVisibility\n}\n\n// Patch an application's mutable fields.\nexport async function updateApplication(\n applicationId: string,\n update: ApplicationUpdate,\n): Promise<void> {\n const client = await getClient()\n await client.request({body: update, method: 'PATCH', uri: `/applications/${applicationId}`})\n}\n\n/** Deploy a new active version to an existing application. */\nexport async function createDeployment(options: {\n applicationId: string\n interfaces: readonly BrettInterface[]\n isAutoUpdating: boolean\n tarball: Gzip\n version: string\n workspaces?: readonly BrettWorkspace[]\n}): Promise<{id: string}> {\n const {applicationId, interfaces, isAutoUpdating, tarball, version, workspaces} = options\n const formData = new FormData()\n formData.append('isAutoUpdating', isAutoUpdating.toString())\n appendDeploymentParts(formData, {interfaces, tarball, version, workspaces})\n return request(`/applications/${applicationId}/deployments`, formData)\n}\n\n/** Soft-deletes the application and all its deployments; already deleted counts as done. */\nexport async function deleteApplication(applicationId: string): Promise<void> {\n const client = await getClient()\n try {\n await client.request({method: 'DELETE', uri: `/applications/${applicationId}`})\n } catch (err) {\n if ((err as {statusCode?: number})?.statusCode !== 404) throw err\n }\n}\n\nfunction appendDeploymentParts(\n formData: FormData,\n {\n interfaces,\n tarball,\n version,\n workspaces,\n }: {\n interfaces: readonly BrettInterface[]\n tarball: Gzip\n version: string\n workspaces?: readonly BrettWorkspace[]\n },\n): void {\n formData.append('version', version)\n appendJson(formData, 'interfaces', interfaces)\n // Studio-only — the server rejects a workspaces part on non-studio types.\n if (workspaces?.length) appendJson(formData, 'workspaces', workspaces)\n formData.append('tarball', tarball, {contentType: 'application/gzip', filename: 'app.tar.gz'})\n}\n\n/** Structured parts must arrive as JSON so the server parses them. */\nfunction appendJson(formData: FormData, name: string, value: unknown): void {\n formData.append(name, JSON.stringify(value), {contentType: 'application/json'})\n}\n\nasync function request<T>(uri: string, formData: FormData): Promise<T> {\n const client = await getClient()\n return client.request({\n body: formData.pipe(new PassThrough()),\n headers: formData.getHeaders(),\n method: 'POST',\n uri,\n })\n}\n"],"names":["PassThrough","getGlobalCliClient","isStaging","FormData","APP_WORKBENCH_API_VERSION","getWorkbenchUrl","organizationId","getApplicationUrl","application","segment","type","id","getClient","apiVersion","requireUser","getApplication","applicationId","client","request","uri","err","statusCode","listApplications","data","query","limit","createApplication","options","isSingleton","projectId","slug","title","visibility","body","undefined","config","studio","method","updateApplication","update","createDeployment","interfaces","isAutoUpdating","tarball","version","workspaces","formData","append","toString","appendDeploymentParts","deleteApplication","appendJson","length","contentType","filename","name","value","JSON","stringify","pipe","headers","getHeaders"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/services/applications.ts"],"sourcesContent":["import {PassThrough} from 'node:stream'\nimport {type Gzip} from 'node:zlib'\n\nimport {type AppVisibility, getGlobalCliClient} from '@sanity/cli-core'\nimport {isStaging} from '@sanity/cli-core/util'\nimport FormData from 'form-data'\n\nimport {type AppInterfaceMetadata, type TileInterfaceMetadata} from '../contract.js'\nimport {APP_WORKBENCH_API_VERSION} from './apiVersion.js'\n\nexport type ApplicationType = 'coreApp' | 'studio'\n\nexport interface Application {\n id: string\n organizationId: string\n slug: string | null\n title: string\n type: ApplicationType\n}\n\ninterface BrettInterfaceBase {\n moduleId: string\n name: string\n title: string\n version: string\n}\n\n/**\n * An interface as Brett stores it, discriminated on `type`. `moduleId` is\n * remote-relative — the host prepends the app's id. Brett assigns the id.\n * @internal\n */\nexport type BrettInterface =\n | (BrettInterfaceBase & {metadata: AppInterfaceMetadata | null; type: 'app'})\n | (BrettInterfaceBase & {metadata: null; type: 'asset_source'})\n | (BrettInterfaceBase & {metadata: null; type: 'panel'})\n | (BrettInterfaceBase & {metadata: null; type: 'worker'})\n | (BrettInterfaceBase & {metadata: TileInterfaceMetadata; type: 'tile'})\n\n/**\n * A resource a deployment may interact with, as Brett stores it. Per-deployment\n * and forbidden for singletons (the server 400s).\n */\nexport interface BrettAccess {\n resourceId: string\n resourceType: 'canvas' | 'dashboard' | 'dataset' | 'media-library'\n}\n\n/** A studio workspace as Brett stores it. */\nexport interface BrettWorkspace {\n dataset: string\n projectId: string\n /** Lexicon schema descriptor id; Brett requires one per workspace. */\n schemaDescriptorId: string\n\n basePath?: string\n icon?: string\n name?: string\n subtitle?: string\n title?: string\n}\n\nexport function getWorkbenchUrl(organizationId: string): string {\n return `https://${organizationId}.${isStaging() ? 'run.sanity.work' : 'sanity.run'}`\n}\n\n/** Where a deployed application is served on its organization's workbench. */\nexport function getApplicationUrl(\n application: Pick<Application, 'id' | 'organizationId' | 'type'>,\n): string {\n const segment = application.type === 'studio' ? 'studio' : 'application'\n return `${getWorkbenchUrl(application.organizationId)}/${segment}/${application.id}`\n}\n\nasync function getClient() {\n return getGlobalCliClient({apiVersion: APP_WORKBENCH_API_VERSION, requireUser: true})\n}\n\nexport async function getApplication(applicationId: string): Promise<Application | null> {\n const client = await getClient()\n try {\n return await client.request({uri: `/applications/${applicationId}`})\n } catch (err) {\n if ((err as {statusCode?: number})?.statusCode === 404) return null\n throw err\n }\n}\n\n/** Every application in an organization, in one page (`limit=none`). */\nexport async function listApplications(organizationId: string): Promise<Application[]> {\n const client = await getClient()\n const {data}: {data: Application[]} = await client.request({\n query: {limit: 'none', organizationId},\n uri: '/applications',\n })\n return data\n}\n\n/**\n * Create an application record (no deployment), so the CLI can build with the\n * returned id, then ship it via {@link createDeployment}.\n */\nexport async function createApplication(options: {\n isSingleton?: boolean\n organizationId: string\n projectId?: string\n slug: string\n title: string\n type: ApplicationType\n visibility?: AppVisibility\n}): Promise<Application> {\n const {isSingleton, organizationId, projectId, slug, title, type, visibility} = options\n const client = await getClient()\n return client.request({\n body: {\n organizationId,\n slug,\n title,\n type,\n ...(isSingleton === undefined ? {} : {isSingleton}),\n ...(visibility ? {visibility} : {}),\n // Studio config is set once, at create — it's immutable on redeploy.\n ...(projectId ? {config: {studio: {projectId}}} : {}),\n },\n method: 'POST',\n uri: `/applications`,\n })\n}\n\n/** Mutable application fields the deploy flow patches after create. */\nexport interface ApplicationUpdate {\n icon?: string | null\n title?: string\n visibility?: AppVisibility\n}\n\n// Patch an application's mutable fields.\nexport async function updateApplication(\n applicationId: string,\n update: ApplicationUpdate,\n): Promise<void> {\n const client = await getClient()\n await client.request({body: update, method: 'PATCH', uri: `/applications/${applicationId}`})\n}\n\n/** Deploy a new active version to an existing application. */\nexport async function createDeployment(options: {\n access?: readonly BrettAccess[]\n applicationId: string\n interfaces: readonly BrettInterface[]\n isAutoUpdating: boolean\n tarball: Gzip\n version: string\n workspaces?: readonly BrettWorkspace[]\n}): Promise<{id: string}> {\n const {access, applicationId, interfaces, isAutoUpdating, tarball, version, workspaces} = options\n const formData = new FormData()\n formData.append('isAutoUpdating', isAutoUpdating.toString())\n appendDeploymentParts(formData, {access, interfaces, tarball, version, workspaces})\n return request(`/applications/${applicationId}/deployments`, formData)\n}\n\n/** Soft-deletes the application and all its deployments; already deleted counts as done. */\nexport async function deleteApplication(applicationId: string): Promise<void> {\n const client = await getClient()\n try {\n await client.request({method: 'DELETE', uri: `/applications/${applicationId}`})\n } catch (err) {\n if ((err as {statusCode?: number})?.statusCode !== 404) throw err\n }\n}\n\nfunction appendDeploymentParts(\n formData: FormData,\n {\n access,\n interfaces,\n tarball,\n version,\n workspaces,\n }: {\n access?: readonly BrettAccess[]\n interfaces: readonly BrettInterface[]\n tarball: Gzip\n version: string\n workspaces?: readonly BrettWorkspace[]\n },\n): void {\n formData.append('version', version)\n appendJson(formData, 'interfaces', interfaces)\n // Studio-only — the server rejects a workspaces part on non-studio types.\n if (workspaces?.length) appendJson(formData, 'workspaces', workspaces)\n // Per-deployment; forbidden for singletons, so callers omit it there.\n if (access?.length) appendJson(formData, 'access', access)\n formData.append('tarball', tarball, {contentType: 'application/gzip', filename: 'app.tar.gz'})\n}\n\n/** Structured parts must arrive as JSON so the server parses them. */\nfunction appendJson(formData: FormData, name: string, value: unknown): void {\n formData.append(name, JSON.stringify(value), {contentType: 'application/json'})\n}\n\nasync function request<T>(uri: string, formData: FormData): Promise<T> {\n const client = await getClient()\n return client.request({\n body: formData.pipe(new PassThrough()),\n headers: formData.getHeaders(),\n method: 'POST',\n uri,\n })\n}\n"],"names":["PassThrough","getGlobalCliClient","isStaging","FormData","APP_WORKBENCH_API_VERSION","getWorkbenchUrl","organizationId","getApplicationUrl","application","segment","type","id","getClient","apiVersion","requireUser","getApplication","applicationId","client","request","uri","err","statusCode","listApplications","data","query","limit","createApplication","options","isSingleton","projectId","slug","title","visibility","body","undefined","config","studio","method","updateApplication","update","createDeployment","access","interfaces","isAutoUpdating","tarball","version","workspaces","formData","append","toString","appendDeploymentParts","deleteApplication","appendJson","length","contentType","filename","name","value","JSON","stringify","pipe","headers","getHeaders"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/workbench-cli",
3
- "version": "1.10.0",
3
+ "version": "2.0.1",
4
4
  "description": "Internal implementation detail of the Sanity CLI's unstable workbench support. Not intended for direct use.",
5
5
  "homepage": "https://github.com/sanity-io/cli",
6
6
  "bugs": "https://github.com/sanity-io/cli/issues",
@@ -53,14 +53,14 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
- "@module-federation/vite": "1.20.1",
56
+ "@module-federation/vite": "1.20.7",
57
57
  "@sanity/types": "^6.7.0",
58
58
  "@vitejs/plugin-react": "^6.0.5",
59
59
  "form-data": "^4.0.5",
60
60
  "tar-fs": "^3.1.2",
61
61
  "vite": "^8.2.0",
62
62
  "zod": "^4.4.3",
63
- "@sanity/cli-core": "^2.8.0"
63
+ "@sanity/cli-core": "^3.0.0"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@eslint/compat": "^2.1.0",