@sanity/workbench-cli 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_exports/build.d.ts +10 -0
- package/dist/_exports/deploy.d.ts +57 -8
- package/dist/_exports/deploy.js +2 -1
- package/dist/_exports/deploy.js.map +1 -1
- package/dist/_exports/dev.d.ts +52 -8
- package/dist/_exports/index.d.ts +10 -2
- package/dist/_exports/undeploy.d.ts +258 -0
- package/dist/_exports/undeploy.js +3 -0
- package/dist/_exports/undeploy.js.map +1 -0
- package/dist/actions/build/vite/plugin.js +5 -0
- package/dist/actions/build/vite/plugin.js.map +1 -1
- package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js +5 -3
- package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js.map +1 -1
- package/dist/actions/build/vite/plugins/plugin-sanity-environment.js +26 -2
- package/dist/actions/build/vite/plugins/plugin-sanity-environment.js.map +1 -1
- package/dist/actions/deploy/buildExposes.js +28 -12
- package/dist/actions/deploy/buildExposes.js.map +1 -1
- package/dist/actions/deploy/checkBuiltOutput.js +4 -3
- package/dist/actions/deploy/checkBuiltOutput.js.map +1 -1
- package/dist/actions/deploy/deployConfig.js +11 -38
- package/dist/actions/deploy/deployConfig.js.map +1 -1
- package/dist/actions/deploy/deployWorkbenchApp.js +5 -81
- package/dist/actions/deploy/deployWorkbenchApp.js.map +1 -1
- package/dist/actions/deploy/getWorkbench.js.map +1 -1
- package/dist/actions/dev/deriveInterfaces.js +40 -27
- package/dist/actions/dev/deriveInterfaces.js.map +1 -1
- package/dist/actions/dev/exposesSetId.js +2 -2
- package/dist/actions/dev/exposesSetId.js.map +1 -1
- package/dist/actions/dev/registry.js +30 -17
- package/dist/actions/dev/registry.js.map +1 -1
- package/dist/actions/undeploy/workbenchUndeployAdapter.js +150 -0
- package/dist/actions/undeploy/workbenchUndeployAdapter.js.map +1 -0
- package/dist/contract.js +32 -0
- package/dist/contract.js.map +1 -1
- package/dist/defineApp.js +11 -1
- package/dist/defineApp.js.map +1 -1
- package/dist/defineView.js.map +1 -1
- package/dist/resolveWorkbenchApp.js +2 -1
- package/dist/resolveWorkbenchApp.js.map +1 -1
- package/dist/services/apiVersion.js.map +1 -0
- package/dist/services/applications.js +101 -0
- package/dist/services/applications.js.map +1 -0
- package/dist/services/installations.js +63 -0
- package/dist/services/installations.js.map +1 -0
- package/package.json +16 -12
- package/dist/actions/deploy/apiVersion.js.map +0 -1
- /package/dist/{actions/deploy → services}/apiVersion.js +0 -0
|
@@ -1,20 +1,44 @@
|
|
|
1
1
|
import { FEDERATION_DIR_NAME } from '../constants.js';
|
|
2
2
|
export function sanityEnvironmentPlugin(options) {
|
|
3
|
+
const { clientInput, input } = options;
|
|
3
4
|
return {
|
|
4
5
|
config () {
|
|
5
6
|
return {
|
|
6
7
|
builder: {
|
|
7
8
|
async buildApp (builder) {
|
|
9
|
+
// `emptyOutDir` is false on both environments and the CLI clears
|
|
10
|
+
// `dist` once up-front, so the SPA and federation outputs coexist
|
|
11
|
+
// without either build wiping the other's files.
|
|
12
|
+
if (clientInput) {
|
|
13
|
+
await builder.build(builder.environments.client);
|
|
14
|
+
}
|
|
8
15
|
await builder.build(builder.environments[FEDERATION_DIR_NAME]);
|
|
9
16
|
}
|
|
10
17
|
},
|
|
11
18
|
environments: {
|
|
19
|
+
...clientInput ? {
|
|
20
|
+
client: {
|
|
21
|
+
build: {
|
|
22
|
+
assetsDir: 'static',
|
|
23
|
+
copyPublicDir: false,
|
|
24
|
+
emptyOutDir: false,
|
|
25
|
+
outDir: `dist`,
|
|
26
|
+
rolldownOptions: {
|
|
27
|
+
input: {
|
|
28
|
+
sanity: clientInput
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
consumer: 'client'
|
|
33
|
+
}
|
|
34
|
+
} : {},
|
|
12
35
|
[FEDERATION_DIR_NAME]: {
|
|
13
36
|
build: {
|
|
14
37
|
copyPublicDir: false,
|
|
38
|
+
emptyOutDir: false,
|
|
15
39
|
outDir: `dist`,
|
|
16
|
-
|
|
17
|
-
input
|
|
40
|
+
rolldownOptions: {
|
|
41
|
+
input
|
|
18
42
|
}
|
|
19
43
|
},
|
|
20
44
|
consumer: 'client'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/actions/build/vite/plugins/plugin-sanity-environment.ts"],"sourcesContent":["import {type Plugin} from 'vite'\n\nimport {FEDERATION_DIR_NAME} from '../constants.js'\n\ninterface EnvironmentOptions {\n input: string\n}\n\nexport function sanityEnvironmentPlugin(options: EnvironmentOptions): Plugin {\n return {\n config() {\n return {\n builder: {\n async buildApp(builder) {\n await builder.build(builder.environments[FEDERATION_DIR_NAME])\n },\n },\n environments: {\n [FEDERATION_DIR_NAME]: {\n build: {\n copyPublicDir: false,\n outDir: `dist`,\n
|
|
1
|
+
{"version":3,"sources":["../../../../../src/actions/build/vite/plugins/plugin-sanity-environment.ts"],"sourcesContent":["import {type Plugin} from 'vite'\n\nimport {FEDERATION_DIR_NAME} from '../constants.js'\n\ninterface EnvironmentOptions {\n input: string\n\n /**\n * When set, also build a standalone `client` SPA environment (its own\n * `index.html` + bootstrap) alongside the federation remote, from this entry.\n * Omitted for a dock-only app or when the workbench-remote SPA is disabled.\n */\n clientInput?: string\n}\n\nexport function sanityEnvironmentPlugin(options: EnvironmentOptions): Plugin {\n const {clientInput, input} = options\n\n return {\n config() {\n return {\n builder: {\n async buildApp(builder) {\n // `emptyOutDir` is false on both environments and the CLI clears\n // `dist` once up-front, so the SPA and federation outputs coexist\n // without either build wiping the other's files.\n if (clientInput) {\n await builder.build(builder.environments.client)\n }\n await builder.build(builder.environments[FEDERATION_DIR_NAME])\n },\n },\n environments: {\n ...(clientInput\n ? {\n client: {\n build: {\n assetsDir: 'static',\n copyPublicDir: false,\n emptyOutDir: false,\n outDir: `dist`,\n rolldownOptions: {input: {sanity: clientInput}},\n },\n consumer: 'client',\n },\n }\n : {}),\n [FEDERATION_DIR_NAME]: {\n build: {\n copyPublicDir: false,\n emptyOutDir: false,\n outDir: `dist`,\n rolldownOptions: {input},\n },\n consumer: 'client',\n },\n },\n }\n },\n name: 'sanity/environment',\n }\n}\n"],"names":["FEDERATION_DIR_NAME","sanityEnvironmentPlugin","options","clientInput","input","config","builder","buildApp","build","environments","client","assetsDir","copyPublicDir","emptyOutDir","outDir","rolldownOptions","sanity","consumer","name"],"mappings":"AAEA,SAAQA,mBAAmB,QAAO,kBAAiB;AAanD,OAAO,SAASC,wBAAwBC,OAA2B;IACjE,MAAM,EAACC,WAAW,EAAEC,KAAK,EAAC,GAAGF;IAE7B,OAAO;QACLG;YACE,OAAO;gBACLC,SAAS;oBACP,MAAMC,UAASD,OAAO;wBACpB,iEAAiE;wBACjE,kEAAkE;wBAClE,iDAAiD;wBACjD,IAAIH,aAAa;4BACf,MAAMG,QAAQE,KAAK,CAACF,QAAQG,YAAY,CAACC,MAAM;wBACjD;wBACA,MAAMJ,QAAQE,KAAK,CAACF,QAAQG,YAAY,CAACT,oBAAoB;oBAC/D;gBACF;gBACAS,cAAc;oBACZ,GAAIN,cACA;wBACEO,QAAQ;4BACNF,OAAO;gCACLG,WAAW;gCACXC,eAAe;gCACfC,aAAa;gCACbC,QAAQ,CAAC,IAAI,CAAC;gCACdC,iBAAiB;oCAACX,OAAO;wCAACY,QAAQb;oCAAW;gCAAC;4BAChD;4BACAc,UAAU;wBACZ;oBACF,IACA,CAAC,CAAC;oBACN,CAACjB,oBAAoB,EAAE;wBACrBQ,OAAO;4BACLI,eAAe;4BACfC,aAAa;4BACbC,QAAQ,CAAC,IAAI,CAAC;4BACdC,iBAAiB;gCAACX;4BAAK;wBACzB;wBACAa,UAAU;oBACZ;gBACF;YACF;QACF;QACAC,MAAM;IACR;AACF"}
|
|
@@ -1,31 +1,47 @@
|
|
|
1
|
+
import { interfaceModuleId } from '../../contract.js';
|
|
1
2
|
/**
|
|
2
3
|
* The interface records deploy sends: the app view (only when `exposesAppView`),
|
|
3
4
|
* every view, and every service.
|
|
4
5
|
* @internal
|
|
5
6
|
*/ export function buildExposes(exposes, { appName, appTitle, exposesAppView, version }) {
|
|
6
|
-
const toRecord = (prefix, decl)=>({
|
|
7
|
-
moduleId: `${prefix}/${decl.name}`,
|
|
8
|
-
name: decl.name,
|
|
9
|
-
title: decl.title ?? decl.name,
|
|
10
|
-
type: decl.type,
|
|
11
|
-
version
|
|
12
|
-
});
|
|
13
7
|
const records = [];
|
|
14
8
|
if (exposesAppView) {
|
|
15
9
|
records.push({
|
|
16
|
-
|
|
10
|
+
metadata: null,
|
|
11
|
+
moduleId: interfaceModuleId('app', appName),
|
|
17
12
|
name: appName,
|
|
18
13
|
title: appTitle,
|
|
19
14
|
type: 'app',
|
|
20
15
|
version
|
|
21
16
|
});
|
|
22
17
|
}
|
|
23
|
-
for (const view of exposes.views ?? [])
|
|
24
|
-
|
|
18
|
+
for (const view of exposes.views ?? []){
|
|
19
|
+
records.push({
|
|
20
|
+
metadata: null,
|
|
21
|
+
moduleId: interfaceModuleId('panel', view.name),
|
|
22
|
+
name: view.name,
|
|
23
|
+
title: view.title ?? view.name,
|
|
24
|
+
type: 'panel',
|
|
25
|
+
version
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
for (const service of exposes.services ?? []){
|
|
29
|
+
records.push({
|
|
30
|
+
metadata: null,
|
|
31
|
+
moduleId: interfaceModuleId('worker', service.name),
|
|
32
|
+
name: service.name,
|
|
33
|
+
title: service.title ?? service.name,
|
|
34
|
+
type: 'worker',
|
|
35
|
+
version
|
|
36
|
+
});
|
|
37
|
+
}
|
|
25
38
|
return records;
|
|
26
39
|
}
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
const label = (item)=>item.title === item.name ? item.name : `${item.title} (${item.name})`;
|
|
41
|
+
/**
|
|
42
|
+
* One `Title (name): src` report line per declared entry point.
|
|
43
|
+
* @internal
|
|
44
|
+
*/ export function summarizeExposeGroup(heading, items) {
|
|
29
45
|
return `${heading}:\n${items.map((item)=>` ${label(item)}: ${item.src}`).join('\n')}`;
|
|
30
46
|
}
|
|
31
47
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/buildExposes.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/buildExposes.ts"],"sourcesContent":["import {interfaceModuleId} from '../../contract.js'\nimport {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\nimport {type BrettInterface} from '../../services/applications.js'\n\ninterface BuildExposesContext {\n appName: string\n appTitle: string\n /** Whether the build exposes the app view (`./App`) — apps with an `entry`, and every studio. */\n exposesAppView: boolean\n version: string\n}\n\n/**\n * The interface records deploy sends: the app view (only when `exposesAppView`),\n * every view, and every service.\n * @internal\n */\nexport function buildExposes(\n exposes: WorkbenchExposes,\n {appName, appTitle, exposesAppView, version}: BuildExposesContext,\n): BrettInterface[] {\n const records: BrettInterface[] = []\n if (exposesAppView) {\n records.push({\n metadata: null,\n moduleId: interfaceModuleId('app', appName),\n name: appName,\n title: appTitle,\n type: 'app',\n version,\n })\n }\n for (const view of exposes.views ?? []) {\n records.push({\n metadata: null,\n moduleId: interfaceModuleId('panel', view.name),\n name: view.name,\n title: view.title ?? view.name,\n type: 'panel',\n version,\n })\n }\n for (const service of exposes.services ?? []) {\n records.push({\n metadata: null,\n moduleId: interfaceModuleId('worker', service.name),\n name: service.name,\n title: service.title ?? service.name,\n type: 'worker',\n version,\n })\n }\n return records\n}\n\n/** A view or service as the deploy report and `--json` output surface it. */\nexport interface DeployedExpose {\n name: string\n src: string\n title: string\n type: string\n}\n\nconst label = (item: {name: string; title: string}) =>\n item.title === item.name ? item.name : `${item.title} (${item.name})`\n\n/**\n * One `Title (name): src` report line per declared entry point.\n * @internal\n */\nexport function summarizeExposeGroup(\n heading: string,\n items: readonly {name: string; src: string; title: string}[],\n): string {\n return `${heading}:\\n${items.map((item) => ` ${label(item)}: ${item.src}`).join('\\n')}`\n}\n\n/**\n * The deploy summary of an app's exposes: the structured records (for `--json`)\n * and one report line per non-empty group (for the human report).\n * @internal\n */\nexport function summarizeExposes({services, views}: WorkbenchExposes): {\n exposes: DeployedExpose[]\n lines: string[]\n} {\n const toExpose = (decl: {\n name: string\n src: string\n title?: string\n type: string\n }): DeployedExpose => ({\n name: decl.name,\n src: decl.src,\n title: decl.title ?? decl.name,\n type: decl.type,\n })\n const viewExposes = (views ?? []).map((view) => toExpose(view))\n const serviceExposes = (services ?? []).map((service) => toExpose(service))\n\n const lines: string[] = []\n if (viewExposes.length > 0) lines.push(summarizeExposeGroup('Views', viewExposes))\n if (serviceExposes.length > 0) lines.push(summarizeExposeGroup('Services', serviceExposes))\n return {exposes: [...viewExposes, ...serviceExposes], lines}\n}\n"],"names":["interfaceModuleId","buildExposes","exposes","appName","appTitle","exposesAppView","version","records","push","metadata","moduleId","name","title","type","view","views","service","services","label","item","summarizeExposeGroup","heading","items","map","src","join","summarizeExposes","toExpose","decl","viewExposes","serviceExposes","lines","length"],"mappings":"AAAA,SAAQA,iBAAiB,QAAO,oBAAmB;AAYnD;;;;CAIC,GACD,OAAO,SAASC,aACdC,OAAyB,EACzB,EAACC,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAEC,OAAO,EAAsB;IAEjE,MAAMC,UAA4B,EAAE;IACpC,IAAIF,gBAAgB;QAClBE,QAAQC,IAAI,CAAC;YACXC,UAAU;YACVC,UAAUV,kBAAkB,OAAOG;YACnCQ,MAAMR;YACNS,OAAOR;YACPS,MAAM;YACNP;QACF;IACF;IACA,KAAK,MAAMQ,QAAQZ,QAAQa,KAAK,IAAI,EAAE,CAAE;QACtCR,QAAQC,IAAI,CAAC;YACXC,UAAU;YACVC,UAAUV,kBAAkB,SAASc,KAAKH,IAAI;YAC9CA,MAAMG,KAAKH,IAAI;YACfC,OAAOE,KAAKF,KAAK,IAAIE,KAAKH,IAAI;YAC9BE,MAAM;YACNP;QACF;IACF;IACA,KAAK,MAAMU,WAAWd,QAAQe,QAAQ,IAAI,EAAE,CAAE;QAC5CV,QAAQC,IAAI,CAAC;YACXC,UAAU;YACVC,UAAUV,kBAAkB,UAAUgB,QAAQL,IAAI;YAClDA,MAAMK,QAAQL,IAAI;YAClBC,OAAOI,QAAQJ,KAAK,IAAII,QAAQL,IAAI;YACpCE,MAAM;YACNP;QACF;IACF;IACA,OAAOC;AACT;AAUA,MAAMW,QAAQ,CAACC,OACbA,KAAKP,KAAK,KAAKO,KAAKR,IAAI,GAAGQ,KAAKR,IAAI,GAAG,GAAGQ,KAAKP,KAAK,CAAC,EAAE,EAAEO,KAAKR,IAAI,CAAC,CAAC,CAAC;AAEvE;;;CAGC,GACD,OAAO,SAASS,qBACdC,OAAe,EACfC,KAA4D;IAE5D,OAAO,GAAGD,QAAQ,GAAG,EAAEC,MAAMC,GAAG,CAAC,CAACJ,OAAS,CAAC,EAAE,EAAED,MAAMC,MAAM,EAAE,EAAEA,KAAKK,GAAG,EAAE,EAAEC,IAAI,CAAC,OAAO;AAC1F;AAEA;;;;CAIC,GACD,OAAO,SAASC,iBAAiB,EAACT,QAAQ,EAAEF,KAAK,EAAmB;IAIlE,MAAMY,WAAW,CAACC,OAKK,CAAA;YACrBjB,MAAMiB,KAAKjB,IAAI;YACfa,KAAKI,KAAKJ,GAAG;YACbZ,OAAOgB,KAAKhB,KAAK,IAAIgB,KAAKjB,IAAI;YAC9BE,MAAMe,KAAKf,IAAI;QACjB,CAAA;IACA,MAAMgB,cAAc,AAACd,CAAAA,SAAS,EAAE,AAAD,EAAGQ,GAAG,CAAC,CAACT,OAASa,SAASb;IACzD,MAAMgB,iBAAiB,AAACb,CAAAA,YAAY,EAAE,AAAD,EAAGM,GAAG,CAAC,CAACP,UAAYW,SAASX;IAElE,MAAMe,QAAkB,EAAE;IAC1B,IAAIF,YAAYG,MAAM,GAAG,GAAGD,MAAMvB,IAAI,CAACY,qBAAqB,SAASS;IACrE,IAAIC,eAAeE,MAAM,GAAG,GAAGD,MAAMvB,IAAI,CAACY,qBAAqB,YAAYU;IAC3E,OAAO;QAAC5B,SAAS;eAAI2B;eAAgBC;SAAe;QAAEC;IAAK;AAC7D"}
|
|
@@ -2,9 +2,10 @@ import { stat } from 'node:fs/promises';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
/**
|
|
4
4
|
* Throws unless `sourceDir` is a directory holding a federation build.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* marker that `sanity build` produced a
|
|
5
|
+
* A workbench build always emits a module-federation remote, and may
|
|
6
|
+
* additionally emit a standalone `index.html` SPA (workbench remotes). Either
|
|
7
|
+
* way `mf-manifest.json` is the reliable marker that `sanity build` produced a
|
|
8
|
+
* federation build, so that — not `index.html` — is what we check for.
|
|
8
9
|
*/ export async function checkBuiltOutput(sourceDir) {
|
|
9
10
|
try {
|
|
10
11
|
const stats = await stat(sourceDir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/checkBuiltOutput.ts"],"sourcesContent":["import {stat} from 'node:fs/promises'\nimport {join} from 'node:path'\n\n/**\n * Throws unless `sourceDir` is a directory holding a federation build.\n *
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/checkBuiltOutput.ts"],"sourcesContent":["import {stat} from 'node:fs/promises'\nimport {join} from 'node:path'\n\n/**\n * Throws unless `sourceDir` is a directory holding a federation build.\n * A workbench build always emits a module-federation remote, and may\n * additionally emit a standalone `index.html` SPA (workbench remotes). Either\n * way `mf-manifest.json` is the reliable marker that `sanity build` produced a\n * federation build, so that — not `index.html` — is what we check for.\n */\nexport async function checkBuiltOutput(sourceDir: string): Promise<void> {\n try {\n const stats = await stat(sourceDir)\n if (!stats.isDirectory()) {\n throw new Error(`\"${sourceDir}\" is not a directory`)\n }\n } catch (err) {\n throw err.code === 'ENOENT' ? new Error(`Directory \"${sourceDir}\" does not exist`) : err\n }\n\n const manifestPath = join(sourceDir, 'mf-manifest.json')\n try {\n await stat(manifestPath)\n } catch (err) {\n throw err.code === 'ENOENT'\n ? new Error(\n `\"${manifestPath}\" does not exist. ` +\n 'The deploy directory must contain a federation build created with \"sanity build\".',\n )\n : err\n }\n}\n"],"names":["stat","join","checkBuiltOutput","sourceDir","stats","isDirectory","Error","err","code","manifestPath"],"mappings":"AAAA,SAAQA,IAAI,QAAO,mBAAkB;AACrC,SAAQC,IAAI,QAAO,YAAW;AAE9B;;;;;;CAMC,GACD,OAAO,eAAeC,iBAAiBC,SAAiB;IACtD,IAAI;QACF,MAAMC,QAAQ,MAAMJ,KAAKG;QACzB,IAAI,CAACC,MAAMC,WAAW,IAAI;YACxB,MAAM,IAAIC,MAAM,CAAC,CAAC,EAAEH,UAAU,oBAAoB,CAAC;QACrD;IACF,EAAE,OAAOI,KAAK;QACZ,MAAMA,IAAIC,IAAI,KAAK,WAAW,IAAIF,MAAM,CAAC,WAAW,EAAEH,UAAU,gBAAgB,CAAC,IAAII;IACvF;IAEA,MAAME,eAAeR,KAAKE,WAAW;IACrC,IAAI;QACF,MAAMH,KAAKS;IACb,EAAE,OAAOF,KAAK;QACZ,MAAMA,IAAIC,IAAI,KAAK,WACf,IAAIF,MACF,CAAC,CAAC,EAAEG,aAAa,kBAAkB,CAAC,GAClC,uFAEJF;IACN;AACF"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { basename, dirname } from 'node:path';
|
|
2
|
-
import { PassThrough } from 'node:stream';
|
|
3
2
|
import { styleText } from 'node:util';
|
|
4
3
|
import { createGzip } from 'node:zlib';
|
|
5
|
-
import {
|
|
6
|
-
import FormData from 'form-data';
|
|
4
|
+
import { subdebug } from '@sanity/cli-core';
|
|
7
5
|
import { pack } from 'tar-fs';
|
|
8
|
-
import {
|
|
6
|
+
import { getWorkbenchUrl } from '../../services/applications.js';
|
|
7
|
+
import { createConfig, resolveSingletonInstallationId } from '../../services/installations.js';
|
|
8
|
+
import { summarizeExposeGroup } from './buildExposes.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,8 +31,7 @@ const debug = subdebug('deploy');
|
|
|
31
31
|
switch(config.appType){
|
|
32
32
|
case 'media-library':
|
|
33
33
|
{
|
|
34
|
-
|
|
35
|
-
return `Media library fields:\n${items}`;
|
|
34
|
+
return summarizeExposeGroup('Media library fields', config.fields);
|
|
36
35
|
}
|
|
37
36
|
default:
|
|
38
37
|
{
|
|
@@ -46,45 +45,19 @@ const debug = subdebug('deploy');
|
|
|
46
45
|
* reaches this mutating step.
|
|
47
46
|
* @internal
|
|
48
47
|
*/ export async function deployConfig(options) {
|
|
49
|
-
const { appType, installationId, output, sourceDir, version } = options;
|
|
48
|
+
const { appType, installationId, organizationId, output, sourceDir, version } = options;
|
|
50
49
|
const tarball = pack(dirname(sourceDir), {
|
|
51
50
|
entries: [
|
|
52
51
|
basename(sourceDir)
|
|
53
52
|
]
|
|
54
53
|
}).pipe(createGzip());
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
contentType: 'application/gzip',
|
|
59
|
-
filename: 'installation-config.tar.gz'
|
|
60
|
-
});
|
|
61
|
-
const client = await getGlobalCliClient({
|
|
62
|
-
apiVersion: APP_WORKBENCH_API_VERSION,
|
|
63
|
-
requireUser: true
|
|
64
|
-
});
|
|
65
|
-
await client.request({
|
|
66
|
-
body: formData.pipe(new PassThrough()),
|
|
67
|
-
headers: formData.getHeaders(),
|
|
68
|
-
method: 'POST',
|
|
69
|
-
uri: `/installations/${installationId}/configs`
|
|
54
|
+
await createConfig(installationId, {
|
|
55
|
+
tarball,
|
|
56
|
+
version
|
|
70
57
|
});
|
|
71
58
|
debug('Deployed config for app type: %s', appType);
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
/** The org's active singleton installation, matched on its slug. */ async function resolveSingletonInstallationId(organizationId, slug) {
|
|
75
|
-
const client = await getGlobalCliClient({
|
|
76
|
-
apiVersion: APP_WORKBENCH_API_VERSION,
|
|
77
|
-
requireUser: true
|
|
78
|
-
});
|
|
79
|
-
// `limit=none` returns every installation in one response, no pagination.
|
|
80
|
-
const { data } = await client.request({
|
|
81
|
-
query: {
|
|
82
|
-
limit: 'none',
|
|
83
|
-
organizationId
|
|
84
|
-
},
|
|
85
|
-
uri: '/installations'
|
|
86
|
-
});
|
|
87
|
-
return data.find((item)=>item.application?.slug === slug)?.id;
|
|
59
|
+
const url = getWorkbenchUrl(organizationId);
|
|
60
|
+
output.log(`\n🚀 ${styleText('bold', 'Success!')} Config deployed to ${styleText('cyan', url)}`);
|
|
88
61
|
}
|
|
89
62
|
|
|
90
63
|
//# sourceMappingURL=deployConfig.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/deployConfig.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {
|
|
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 './buildExposes.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,oBAAmB;AAEtD,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,93 +1,16 @@
|
|
|
1
1
|
import { basename, dirname } from 'node:path';
|
|
2
|
-
import { PassThrough } from 'node:stream';
|
|
3
2
|
import { createGzip } from 'node:zlib';
|
|
4
|
-
import { exitCodes
|
|
3
|
+
import { exitCodes } from '@sanity/cli-core';
|
|
5
4
|
import { spinner } from '@sanity/cli-core/ux';
|
|
6
|
-
import FormData from 'form-data';
|
|
7
5
|
import { pack } from 'tar-fs';
|
|
8
|
-
import {
|
|
9
|
-
export async function getApplication(applicationId) {
|
|
10
|
-
const client = await getGlobalCliClient({
|
|
11
|
-
apiVersion: APP_WORKBENCH_API_VERSION,
|
|
12
|
-
requireUser: true
|
|
13
|
-
});
|
|
14
|
-
try {
|
|
15
|
-
return await client.request({
|
|
16
|
-
uri: `/applications/${applicationId}`
|
|
17
|
-
});
|
|
18
|
-
} catch (err) {
|
|
19
|
-
if (err?.statusCode === 404) return null;
|
|
20
|
-
throw err;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
/** Create an application and its first deployment in one call. */ export async function createApplication(options) {
|
|
24
|
-
const { interfaces, isSingleton, organizationId, projectId, slug, tarball, title, type, version, workspaces } = options;
|
|
25
|
-
const formData = new FormData();
|
|
26
|
-
formData.append('type', type);
|
|
27
|
-
formData.append('title', title);
|
|
28
|
-
formData.append('organizationId', organizationId);
|
|
29
|
-
formData.append('slug', slug);
|
|
30
|
-
if (isSingleton !== undefined) formData.append('isSingleton', String(isSingleton));
|
|
31
|
-
// Studio config is set once, at create — it's immutable on redeploy.
|
|
32
|
-
if (projectId) appendJson(formData, 'config', {
|
|
33
|
-
studio: {
|
|
34
|
-
projectId
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
appendDeploymentParts(formData, {
|
|
38
|
-
interfaces,
|
|
39
|
-
tarball,
|
|
40
|
-
version,
|
|
41
|
-
workspaces
|
|
42
|
-
});
|
|
43
|
-
return request(`/applications`, formData);
|
|
44
|
-
}
|
|
45
|
-
/** Deploy a new active version to an existing application. */ export async function createDeployment(options) {
|
|
46
|
-
const { applicationId, interfaces, isAutoUpdating, tarball, version, workspaces } = options;
|
|
47
|
-
const formData = new FormData();
|
|
48
|
-
formData.append('isAutoUpdating', isAutoUpdating.toString());
|
|
49
|
-
appendDeploymentParts(formData, {
|
|
50
|
-
interfaces,
|
|
51
|
-
tarball,
|
|
52
|
-
version,
|
|
53
|
-
workspaces
|
|
54
|
-
});
|
|
55
|
-
return request(`/applications/${applicationId}/deployments`, formData);
|
|
56
|
-
}
|
|
57
|
-
function appendDeploymentParts(formData, { interfaces, tarball, version, workspaces }) {
|
|
58
|
-
formData.append('version', version);
|
|
59
|
-
appendJson(formData, 'interfaces', interfaces);
|
|
60
|
-
// Studio-only — the server rejects a workspaces part on non-studio types.
|
|
61
|
-
if (workspaces?.length) appendJson(formData, 'workspaces', workspaces);
|
|
62
|
-
formData.append('tarball', tarball, {
|
|
63
|
-
contentType: 'application/gzip',
|
|
64
|
-
filename: 'app.tar.gz'
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
/** Structured parts must arrive as JSON so the server parses them. */ function appendJson(formData, name, value) {
|
|
68
|
-
formData.append(name, JSON.stringify(value), {
|
|
69
|
-
contentType: 'application/json'
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
async function request(uri, formData) {
|
|
73
|
-
const client = await getGlobalCliClient({
|
|
74
|
-
apiVersion: APP_WORKBENCH_API_VERSION,
|
|
75
|
-
requireUser: true
|
|
76
|
-
});
|
|
77
|
-
return client.request({
|
|
78
|
-
body: formData.pipe(new PassThrough()),
|
|
79
|
-
headers: formData.getHeaders(),
|
|
80
|
-
method: 'POST',
|
|
81
|
-
uri
|
|
82
|
-
});
|
|
83
|
-
}
|
|
6
|
+
import { createApplication, createDeployment } from '../../services/applications.js';
|
|
84
7
|
/**
|
|
85
8
|
* Deploy a workbench coreApp through Brett: redeploy when `appId` is set,
|
|
86
9
|
* otherwise create the application at `slug`. Returns the application id for the
|
|
87
10
|
* shell to report.
|
|
88
11
|
* @internal
|
|
89
12
|
*/ export async function deployCoreApp(options) {
|
|
90
|
-
const { appId, interfaces, isAutoUpdating, isSingleton, organizationId, slug, sourceDir, title, version } = options;
|
|
13
|
+
const { appId, interfaces, isAutoUpdating, isSingleton, organizationId, slug, sourceDir, title, version, visibility } = options;
|
|
91
14
|
const tarball = pack(dirname(sourceDir), {
|
|
92
15
|
entries: [
|
|
93
16
|
basename(sourceDir)
|
|
@@ -116,7 +39,8 @@ async function request(uri, formData) {
|
|
|
116
39
|
tarball,
|
|
117
40
|
title,
|
|
118
41
|
type: 'coreApp',
|
|
119
|
-
version
|
|
42
|
+
version,
|
|
43
|
+
visibility
|
|
120
44
|
});
|
|
121
45
|
spin.succeed();
|
|
122
46
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/deployWorkbenchApp.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {PassThrough} from 'node:stream'\nimport {createGzip, type Gzip} from 'node:zlib'\n\nimport {exitCodes, getGlobalCliClient, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport FormData from 'form-data'\nimport {pack} from 'tar-fs'\n\nimport {APP_WORKBENCH_API_VERSION} from './apiVersion.js'\nimport {type BrettInterface} from './buildExposes.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\n/** A studio workspace as Brett stores it. */\nexport interface BrettWorkspace {\n dataset: string\n projectId: string\n\n basePath?: string\n icon?: string\n name?: string\n subtitle?: string\n title?: string\n}\n\nexport async function getApplication(applicationId: string): Promise<Application | null> {\n const client = await getGlobalCliClient({\n apiVersion: APP_WORKBENCH_API_VERSION,\n requireUser: true,\n })\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/** Create an application and its first deployment in one call. */\nexport async function createApplication(options: {\n interfaces: readonly BrettInterface[]\n isSingleton?: boolean\n organizationId: string\n projectId?: string\n slug: string\n tarball: Gzip\n title: string\n type: ApplicationType\n version: string\n workspaces?: readonly BrettWorkspace[]\n}): Promise<Application> {\n const {\n interfaces,\n isSingleton,\n organizationId,\n projectId,\n slug,\n tarball,\n title,\n type,\n version,\n workspaces,\n } = options\n const formData = new FormData()\n formData.append('type', type)\n formData.append('title', title)\n formData.append('organizationId', organizationId)\n formData.append('slug', slug)\n if (isSingleton !== undefined) formData.append('isSingleton', String(isSingleton))\n // Studio config is set once, at create — it's immutable on redeploy.\n if (projectId) appendJson(formData, 'config', {studio: {projectId}})\n appendDeploymentParts(formData, {interfaces, tarball, version, workspaces})\n return request(`/applications`, formData)\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\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 getGlobalCliClient({\n apiVersion: APP_WORKBENCH_API_VERSION,\n requireUser: true,\n })\n return client.request({\n body: formData.pipe(new PassThrough()),\n headers: formData.getHeaders(),\n method: 'POST',\n uri,\n })\n}\n\n/**\n * Deploy a workbench coreApp through Brett: redeploy when `appId` is set,\n * otherwise create the application at `slug`. Returns the application id for the\n * shell to report.\n * @internal\n */\nexport async function deployCoreApp(options: {\n appId: string | undefined\n interfaces: readonly BrettInterface[]\n isAutoUpdating: boolean\n isSingleton?: boolean\n organizationId: string\n slug: string\n sourceDir: string\n title: string\n version: string\n}): Promise<{applicationId: string}> {\n const {\n appId,\n interfaces,\n isAutoUpdating,\n isSingleton,\n organizationId,\n slug,\n sourceDir,\n title,\n version,\n } = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner('Deploying...').start()\n try {\n if (appId) {\n await createDeployment({applicationId: appId, interfaces, isAutoUpdating, tarball, version})\n spin.succeed()\n return {applicationId: appId}\n }\n\n const {id} = await createApplication({\n interfaces,\n isSingleton,\n organizationId,\n slug,\n tarball,\n title,\n type: 'coreApp',\n version,\n })\n spin.succeed()\n return {applicationId: id}\n } catch (error) {\n spin.clear()\n throw error\n }\n}\n\n/**\n * Deploy a workbench studio through Brett: redeploy when `appId` is set,\n * otherwise create the studio at `studioHost`. Returns the application id for\n * the shell to report; a missing `studioHost` on create is a usage error.\n * @internal\n */\nexport async function deployStudio(options: {\n appId: string | undefined\n interfaces: readonly BrettInterface[]\n isAutoUpdating: boolean\n organizationId: string\n output: Output\n projectId: string | undefined\n sourceDir: string\n studioHost: string | undefined\n title: string\n version: string\n workspaces: readonly BrettWorkspace[]\n}): Promise<{applicationId: string}> {\n const {\n appId,\n interfaces,\n isAutoUpdating,\n organizationId,\n output,\n projectId,\n sourceDir,\n studioHost,\n title,\n version,\n workspaces,\n } = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner('Deploying to sanity.studio').start()\n try {\n if (appId) {\n await createDeployment({\n applicationId: appId,\n interfaces,\n isAutoUpdating,\n tarball,\n version,\n workspaces,\n })\n spin.succeed()\n return {applicationId: appId}\n }\n\n if (!studioHost) {\n spin.fail()\n return output.error(\n 'No studio hostname configured. Set `studioHost` in sanity.cli.ts to create a studio.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const application = await createApplication({\n interfaces,\n organizationId,\n projectId,\n slug: studioHost,\n tarball,\n title,\n type: 'studio',\n version,\n workspaces,\n })\n spin.succeed()\n return {applicationId: application.id}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n"],"names":["basename","dirname","PassThrough","createGzip","exitCodes","getGlobalCliClient","spinner","FormData","pack","APP_WORKBENCH_API_VERSION","getApplication","applicationId","client","apiVersion","requireUser","request","uri","err","statusCode","createApplication","options","interfaces","isSingleton","organizationId","projectId","slug","tarball","title","type","version","workspaces","formData","append","undefined","String","appendJson","studio","appendDeploymentParts","createDeployment","isAutoUpdating","toString","length","contentType","filename","name","value","JSON","stringify","body","pipe","headers","getHeaders","method","deployCoreApp","appId","sourceDir","entries","spin","start","succeed","id","error","clear","deployStudio","output","studioHost","fail","exit","USAGE_ERROR","application"],"mappings":"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"}
|
|
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, exitCodes, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {pack} from 'tar-fs'\n\nimport {\n type BrettInterface,\n type BrettWorkspace,\n createApplication,\n createDeployment,\n} from '../../services/applications.js'\n\n/**\n * Deploy a workbench coreApp through Brett: redeploy when `appId` is set,\n * otherwise create the application at `slug`. Returns the application id for the\n * shell to report.\n * @internal\n */\nexport async function deployCoreApp(options: {\n appId: string | undefined\n interfaces: readonly BrettInterface[]\n isAutoUpdating: boolean\n isSingleton?: boolean\n organizationId: string\n slug: string\n sourceDir: string\n title: string\n version: string\n visibility?: AppVisibility\n}): Promise<{applicationId: string}> {\n const {\n appId,\n interfaces,\n isAutoUpdating,\n isSingleton,\n organizationId,\n slug,\n sourceDir,\n title,\n version,\n visibility,\n } = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner('Deploying...').start()\n try {\n if (appId) {\n await createDeployment({applicationId: appId, interfaces, isAutoUpdating, tarball, version})\n spin.succeed()\n return {applicationId: appId}\n }\n\n const {id} = await createApplication({\n interfaces,\n isSingleton,\n organizationId,\n slug,\n tarball,\n title,\n type: 'coreApp',\n version,\n visibility,\n })\n spin.succeed()\n return {applicationId: id}\n } catch (error) {\n spin.clear()\n throw error\n }\n}\n\n/**\n * Deploy a workbench studio through Brett: redeploy when `appId` is set,\n * otherwise create the studio at `studioHost`. Returns the application id for\n * the shell to report; a missing `studioHost` on create is a usage error.\n * @internal\n */\nexport async function deployStudio(options: {\n appId: string | undefined\n interfaces: readonly BrettInterface[]\n isAutoUpdating: boolean\n organizationId: string\n output: Output\n projectId: string | undefined\n sourceDir: string\n studioHost: string | undefined\n title: string\n version: string\n workspaces: readonly BrettWorkspace[]\n}): Promise<{applicationId: string}> {\n const {\n appId,\n interfaces,\n isAutoUpdating,\n organizationId,\n output,\n projectId,\n sourceDir,\n studioHost,\n title,\n version,\n workspaces,\n } = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner('Deploying to sanity.studio').start()\n try {\n if (appId) {\n await createDeployment({\n applicationId: appId,\n interfaces,\n isAutoUpdating,\n tarball,\n version,\n workspaces,\n })\n spin.succeed()\n return {applicationId: appId}\n }\n\n if (!studioHost) {\n spin.fail()\n return output.error(\n 'No studio hostname configured. Set `studioHost` in sanity.cli.ts to create a studio.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const application = await createApplication({\n interfaces,\n organizationId,\n projectId,\n slug: studioHost,\n tarball,\n title,\n type: 'studio',\n version,\n workspaces,\n })\n spin.succeed()\n return {applicationId: application.id}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n"],"names":["basename","dirname","createGzip","exitCodes","spinner","pack","createApplication","createDeployment","deployCoreApp","options","appId","interfaces","isAutoUpdating","isSingleton","organizationId","slug","sourceDir","title","version","visibility","tarball","entries","pipe","spin","start","applicationId","succeed","id","type","error","clear","deployStudio","output","projectId","studioHost","workspaces","fail","exit","USAGE_ERROR","application"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAC3C,SAAQC,UAAU,QAAO,YAAW;AAEpC,SAA4BC,SAAS,QAAoB,mBAAkB;AAC3E,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,IAAI,QAAO,SAAQ;AAE3B,SAGEC,iBAAiB,EACjBC,gBAAgB,QACX,iCAAgC;AAEvC;;;;;CAKC,GACD,OAAO,eAAeC,cAAcC,OAWnC;IACC,MAAM,EACJC,KAAK,EACLC,UAAU,EACVC,cAAc,EACdC,WAAW,EACXC,cAAc,EACdC,IAAI,EACJC,SAAS,EACTC,KAAK,EACLC,OAAO,EACPC,UAAU,EACX,GAAGV;IACJ,MAAMW,UAAUf,KAAKJ,QAAQe,YAAY;QAACK,SAAS;YAACrB,SAASgB;SAAW;IAAA,GAAGM,IAAI,CAACpB;IAEhF,MAAMqB,OAAOnB,QAAQ,gBAAgBoB,KAAK;IAC1C,IAAI;QACF,IAAId,OAAO;YACT,MAAMH,iBAAiB;gBAACkB,eAAef;gBAAOC;gBAAYC;gBAAgBQ;gBAASF;YAAO;YAC1FK,KAAKG,OAAO;YACZ,OAAO;gBAACD,eAAef;YAAK;QAC9B;QAEA,MAAM,EAACiB,EAAE,EAAC,GAAG,MAAMrB,kBAAkB;YACnCK;YACAE;YACAC;YACAC;YACAK;YACAH;YACAW,MAAM;YACNV;YACAC;QACF;QACAI,KAAKG,OAAO;QACZ,OAAO;YAACD,eAAeE;QAAE;IAC3B,EAAE,OAAOE,OAAO;QACdN,KAAKO,KAAK;QACV,MAAMD;IACR;AACF;AAEA;;;;;CAKC,GACD,OAAO,eAAeE,aAAatB,OAYlC;IACC,MAAM,EACJC,KAAK,EACLC,UAAU,EACVC,cAAc,EACdE,cAAc,EACdkB,MAAM,EACNC,SAAS,EACTjB,SAAS,EACTkB,UAAU,EACVjB,KAAK,EACLC,OAAO,EACPiB,UAAU,EACX,GAAG1B;IACJ,MAAMW,UAAUf,KAAKJ,QAAQe,YAAY;QAACK,SAAS;YAACrB,SAASgB;SAAW;IAAA,GAAGM,IAAI,CAACpB;IAEhF,MAAMqB,OAAOnB,QAAQ,8BAA8BoB,KAAK;IACxD,IAAI;QACF,IAAId,OAAO;YACT,MAAMH,iBAAiB;gBACrBkB,eAAef;gBACfC;gBACAC;gBACAQ;gBACAF;gBACAiB;YACF;YACAZ,KAAKG,OAAO;YACZ,OAAO;gBAACD,eAAef;YAAK;QAC9B;QAEA,IAAI,CAACwB,YAAY;YACfX,KAAKa,IAAI;YACT,OAAOJ,OAAOH,KAAK,CACjB,wFACA;gBAACQ,MAAMlC,UAAUmC,WAAW;YAAA;QAEhC;QAEA,MAAMC,cAAc,MAAMjC,kBAAkB;YAC1CK;YACAG;YACAmB;YACAlB,MAAMmB;YACNd;YACAH;YACAW,MAAM;YACNV;YACAiB;QACF;QACAZ,KAAKG,OAAO;QACZ,OAAO;YAACD,eAAec,YAAYZ,EAAE;QAAA;IACvC,EAAE,OAAOE,OAAO;QACdN,KAAKa,IAAI;QACT,MAAMP;IACR;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/getWorkbench.ts"],"sourcesContent":["// The deploy command's view of a workbench app: the resolved interfaces plus\n// the deploy-time guards that need the app's declarations. `sanity deploy` calls\n// `getWorkbench(config)` once and either gets `null` (plain project — normal\n// deploy) or an object it asks to validate the app before shipping.\n\nimport {type CliConfig} from '@sanity/cli-core'\n\nimport {type ResolvedWorkbenchApp, resolveWorkbenchApp} from '../../resolveWorkbenchApp.js'\nimport {buildViewDeploymentPayload, type ViewDeploymentPayload} from './viewDeployment.js'\n\
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/getWorkbench.ts"],"sourcesContent":["// The deploy command's view of a workbench app: the resolved interfaces plus\n// the deploy-time guards that need the app's declarations. `sanity deploy` calls\n// `getWorkbench(config)` once and either gets `null` (plain project — normal\n// deploy) or an object it asks to validate the app before shipping.\n\nimport {type CliConfig} from '@sanity/cli-core'\n\nimport {type ResolvedWorkbenchApp, resolveWorkbenchApp} from '../../resolveWorkbenchApp.js'\nimport {buildViewDeploymentPayload, type ViewDeploymentPayload} from './viewDeployment.js'\n\nexport interface DeployableWorkbenchApp extends ResolvedWorkbenchApp {\n /**\n * Throws when the app exposes nothing (no entry, view, service, or config) —\n * the remote would have nothing to load. Gated before any prompt or API call.\n */\n assertDeployable(): void\n /**\n * Validates the app's declared views into the application-service payload.\n * Throws when a view declaration is malformed.\n */\n buildViewDeploymentPayload(applicationId: string): ViewDeploymentPayload\n /**\n * A singleton (the Media Library) that carries an config — deploy\n * persists the config to the org's installation. Independent of the interfaces,\n * which register regardless; non-singletons never carry a config.\n */\n deploySingletonConfig: boolean\n /** Declares something to host as an application — an entry, view, or service. */\n hasInterfaces: boolean\n}\n\nexport function getWorkbench(\n cliConfig: CliConfig | null | undefined,\n): DeployableWorkbenchApp | null {\n const app = resolveWorkbenchApp(cliConfig)\n if (!app) return null\n\n const {config, entry, isSingleton, services, views} = app\n\n return {\n ...app,\n\n deploySingletonConfig: !!isSingleton && !!config,\n hasInterfaces: !!entry || views.length > 0 || services.length > 0,\n\n assertDeployable() {\n if (!entry && views.length === 0 && services.length === 0 && !config) {\n throw new Error(\n 'Nothing to deploy: the app declares no entry, views, services or config. ' +\n 'Add at least one to the app config.',\n )\n }\n },\n\n buildViewDeploymentPayload(applicationId) {\n return buildViewDeploymentPayload({applicationId, views})\n },\n }\n}\n"],"names":["resolveWorkbenchApp","buildViewDeploymentPayload","getWorkbench","cliConfig","app","config","entry","isSingleton","services","views","deploySingletonConfig","hasInterfaces","length","assertDeployable","Error","applicationId"],"mappings":"AAAA,6EAA6E;AAC7E,iFAAiF;AACjF,6EAA6E;AAC7E,oEAAoE;AAIpE,SAAmCA,mBAAmB,QAAO,+BAA8B;AAC3F,SAAQC,0BAA0B,QAAmC,sBAAqB;AAuB1F,OAAO,SAASC,aACdC,SAAuC;IAEvC,MAAMC,MAAMJ,oBAAoBG;IAChC,IAAI,CAACC,KAAK,OAAO;IAEjB,MAAM,EAACC,MAAM,EAAEC,KAAK,EAAEC,WAAW,EAAEC,QAAQ,EAAEC,KAAK,EAAC,GAAGL;IAEtD,OAAO;QACL,GAAGA,GAAG;QAENM,uBAAuB,CAAC,CAACH,eAAe,CAAC,CAACF;QAC1CM,eAAe,CAAC,CAACL,SAASG,MAAMG,MAAM,GAAG,KAAKJ,SAASI,MAAM,GAAG;QAEhEC;YACE,IAAI,CAACP,SAASG,MAAMG,MAAM,KAAK,KAAKJ,SAASI,MAAM,KAAK,KAAK,CAACP,QAAQ;gBACpE,MAAM,IAAIS,MACR,8EACE;YAEN;QACF;QAEAb,4BAA2Bc,aAAa;YACtC,OAAOd,2BAA2B;gBAACc;gBAAeN;YAAK;QACzD;IACF;AACF"}
|
|
@@ -1,40 +1,53 @@
|
|
|
1
1
|
import { hash } from 'node:crypto';
|
|
2
|
-
import { MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION, SERVICE_CONTRACT_VERSION, VIEW_CONTRACT_VERSION } from '../../contract.js';
|
|
2
|
+
import { interfaceModuleId, MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION, SERVICE_CONTRACT_VERSION, VIEW_CONTRACT_VERSION } from '../../contract.js';
|
|
3
3
|
import { isWorkbenchApp, readConfig } from '../../defineApp.js';
|
|
4
4
|
/**
|
|
5
|
-
* Map a workbench app's declarations to
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Map a workbench app's declarations to its registry interface records:
|
|
6
|
+
* `views` → panels, `services` → workers, `entry` → the `app` view. Each mirrors
|
|
7
|
+
* a deployed record so the workbench loads a local interface like a deployed one.
|
|
8
8
|
* `undefined` for a non-branded app; a studio that declares `entry` is rejected
|
|
9
|
-
* (studio app views
|
|
10
|
-
* interface — see {@link deriveConfigs}. `version` is the contract version the
|
|
11
|
-
* interface's generated module exports, known before the module runs; the app
|
|
12
|
-
* view has no versioned contract, so it carries none.
|
|
9
|
+
* (studio app views aren't implemented yet).
|
|
13
10
|
*/ export function deriveInterfaces(app, options) {
|
|
14
11
|
if (!isWorkbenchApp(app)) return undefined;
|
|
15
12
|
if (!options.isApp && app.entry !== undefined) {
|
|
16
13
|
throw new Error('App views for studios are not implemented yet');
|
|
17
14
|
}
|
|
15
|
+
const interfaceId = (type, name)=>`${app.name}-${type}-${name}`;
|
|
16
|
+
const views = (app.views ?? []).map((view)=>({
|
|
17
|
+
id: interfaceId('panel', view.name),
|
|
18
|
+
metadata: null,
|
|
19
|
+
moduleId: interfaceModuleId('panel', view.name),
|
|
20
|
+
name: view.name,
|
|
21
|
+
src: view.src,
|
|
22
|
+
title: view.title ?? view.name,
|
|
23
|
+
type: 'panel',
|
|
24
|
+
version: String(VIEW_CONTRACT_VERSION)
|
|
25
|
+
}));
|
|
26
|
+
const services = (app.services ?? []).map((service)=>({
|
|
27
|
+
id: interfaceId('worker', service.name),
|
|
28
|
+
metadata: null,
|
|
29
|
+
moduleId: interfaceModuleId('worker', service.name),
|
|
30
|
+
name: service.name,
|
|
31
|
+
src: service.src,
|
|
32
|
+
title: service.title ?? service.name,
|
|
33
|
+
type: 'worker',
|
|
34
|
+
version: String(SERVICE_CONTRACT_VERSION)
|
|
35
|
+
}));
|
|
36
|
+
const appView = app.entry === undefined ? [] : [
|
|
37
|
+
{
|
|
38
|
+
id: interfaceId('app', app.name),
|
|
39
|
+
metadata: null,
|
|
40
|
+
moduleId: interfaceModuleId('app', app.name),
|
|
41
|
+
name: app.name,
|
|
42
|
+
src: app.entry,
|
|
43
|
+
title: app.title,
|
|
44
|
+
type: 'app'
|
|
45
|
+
}
|
|
46
|
+
];
|
|
18
47
|
return [
|
|
19
|
-
...
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
name: view.name,
|
|
23
|
-
version: VIEW_CONTRACT_VERSION
|
|
24
|
-
})) ?? [],
|
|
25
|
-
...app.services?.map((service)=>({
|
|
26
|
-
entry_point: service.src,
|
|
27
|
-
interface_type: service.type,
|
|
28
|
-
name: service.name,
|
|
29
|
-
version: SERVICE_CONTRACT_VERSION
|
|
30
|
-
})) ?? [],
|
|
31
|
-
...app.entry === undefined ? [] : [
|
|
32
|
-
{
|
|
33
|
-
entry_point: app.entry,
|
|
34
|
-
interface_type: 'app',
|
|
35
|
-
name: app.name
|
|
36
|
-
}
|
|
37
|
-
]
|
|
48
|
+
...views,
|
|
49
|
+
...services,
|
|
50
|
+
...appView
|
|
38
51
|
];
|
|
39
52
|
}
|
|
40
53
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/dev/deriveInterfaces.ts"],"sourcesContent":["import {hash} from 'node:crypto'\n\nimport {type CliConfig} from '@sanity/cli-core'\n\nimport {\n MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION,\n SERVICE_CONTRACT_VERSION,\n VIEW_CONTRACT_VERSION,\n} from '../../contract.js'\nimport {isWorkbenchApp, readConfig} from '../../defineApp.js'\nimport {type DevServerManifest} from './registry.js'\n\n/** One forwarded interface record on the dev-server registry entry. */\nexport type DevServerInterface = NonNullable<DevServerManifest['interfaces']>[number]\n\n/** One forwarded config on the dev-server registry entry. */\nexport type DevServerConfig = NonNullable<DevServerManifest['configs']>[number]\n\n/**\n * Map a workbench app's declarations to
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/deriveInterfaces.ts"],"sourcesContent":["import {hash} from 'node:crypto'\n\nimport {type CliConfig} from '@sanity/cli-core'\n\nimport {\n interfaceModuleId,\n MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION,\n SERVICE_CONTRACT_VERSION,\n VIEW_CONTRACT_VERSION,\n} from '../../contract.js'\nimport {isWorkbenchApp, readConfig} from '../../defineApp.js'\nimport {type DevServerManifest} from './registry.js'\n\n/** One forwarded interface record on the dev-server registry entry. */\nexport type DevServerInterface = NonNullable<DevServerManifest['interfaces']>[number]\n\n/** One forwarded config on the dev-server registry entry. */\nexport type DevServerConfig = NonNullable<DevServerManifest['configs']>[number]\n\n/**\n * Map a workbench app's declarations to its registry interface records:\n * `views` → panels, `services` → workers, `entry` → the `app` view. Each mirrors\n * a deployed record so the workbench loads a local interface like a deployed one.\n * `undefined` for a non-branded app; a studio that declares `entry` is rejected\n * (studio app views aren't implemented yet).\n */\nexport function deriveInterfaces(\n app: CliConfig['app'],\n options: {isApp: boolean},\n): DevServerInterface[] | undefined {\n if (!isWorkbenchApp(app)) return undefined\n\n if (!options.isApp && app.entry !== undefined) {\n throw new Error('App views for studios are not implemented yet')\n }\n\n const interfaceId = (type: string, name: string): string => `${app.name}-${type}-${name}`\n\n const views = (app.views ?? []).map(\n (view): DevServerInterface => ({\n id: interfaceId('panel', view.name),\n metadata: null,\n moduleId: interfaceModuleId('panel', view.name),\n name: view.name,\n src: view.src,\n title: view.title ?? view.name,\n type: 'panel',\n version: String(VIEW_CONTRACT_VERSION),\n }),\n )\n\n const services = (app.services ?? []).map(\n (service): DevServerInterface => ({\n id: interfaceId('worker', service.name),\n metadata: null,\n moduleId: interfaceModuleId('worker', service.name),\n name: service.name,\n src: service.src,\n title: service.title ?? service.name,\n type: 'worker',\n version: String(SERVICE_CONTRACT_VERSION),\n }),\n )\n\n const appView: DevServerInterface[] =\n app.entry === undefined\n ? []\n : [\n {\n id: interfaceId('app', app.name),\n metadata: null,\n moduleId: interfaceModuleId('app', app.name),\n name: app.name,\n src: app.entry,\n title: app.title,\n type: 'app',\n },\n ]\n\n return [...views, ...services, ...appView]\n}\n\n/**\n * The named source files a config's generated module is built from, dispatched\n * per app type — the projection the exposes-set id keys on, so the generic HMR\n * tracker owns none of the per-type shape. Throws on an app type it can't\n * handle, so a new config family has to register its shape here.\n */\nexport function deriveConfigEntries(config: DevServerConfig): {name: string; src: string}[] {\n switch (config.appType) {\n case 'media-library': {\n return config.fields.map((field) => ({name: field.name, src: field.src}))\n }\n default: {\n throw new Error(`Cannot derive entries for unknown config appType: ${config.appType}`)\n }\n }\n}\n\n/**\n * The fields' schema *values* can't serialize — the workbench loads them from\n * the federation module. `src` stays on so the exposes-set id keys on it and a\n * repoint rebuilds. `appType` routes the config to the singleton (no app id to\n * key on). `id` is a content hash of the entry — it fills the\n * installation-config id slot deployed apps get from the applications API,\n * and the workbench keys change detection on it. `version` is the config\n * contract version the generated module exports, known before the module runs.\n */\nexport function deriveConfigs(app: CliConfig['app']): DevServerConfig[] {\n if (!isWorkbenchApp(app)) return []\n const config = readConfig(app)\n if (!config) return []\n const entry = {\n appType: config.appType,\n fields: config.fields.map((field) => ({\n name: field.name,\n public: field.public,\n src: field.src,\n title: field.title,\n })),\n moduleName: app.name,\n version: MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION,\n }\n return [{...entry, id: hash('sha1', JSON.stringify(entry))}]\n}\n"],"names":["hash","interfaceModuleId","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","SERVICE_CONTRACT_VERSION","VIEW_CONTRACT_VERSION","isWorkbenchApp","readConfig","deriveInterfaces","app","options","undefined","isApp","entry","Error","interfaceId","type","name","views","map","view","id","metadata","moduleId","src","title","version","String","services","service","appView","deriveConfigEntries","config","appType","fields","field","deriveConfigs","public","moduleName","JSON","stringify"],"mappings":"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"}
|
|
@@ -8,9 +8,9 @@ import { deriveConfigEntries } from './deriveInterfaces.js';
|
|
|
8
8
|
*/ export function exposesSetId({ configs, interfaces }) {
|
|
9
9
|
const keys = [
|
|
10
10
|
...(interfaces ?? []).map((iface)=>[
|
|
11
|
-
iface.
|
|
11
|
+
iface.type,
|
|
12
12
|
iface.name,
|
|
13
|
-
iface.
|
|
13
|
+
iface.src
|
|
14
14
|
].join('::')),
|
|
15
15
|
...(configs ?? []).flatMap((config)=>[
|
|
16
16
|
[
|