@sanity/workbench-cli 1.1.2 → 1.2.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 +76 -8
- package/dist/_exports/build.js.map +1 -1
- package/dist/_exports/deploy.d.ts +151 -12
- package/dist/_exports/deploy.js +2 -3
- package/dist/_exports/deploy.js.map +1 -1
- package/dist/_exports/dev.d.ts +22 -0
- package/dist/_exports/index.d.ts +82 -31
- package/dist/_exports/index.js +1 -11
- package/dist/_exports/index.js.map +1 -1
- package/dist/actions/build/artifact.js +8 -7
- package/dist/actions/build/artifact.js.map +1 -1
- package/dist/actions/build/configs/artifact.js +45 -0
- package/dist/actions/build/configs/artifact.js.map +1 -0
- package/dist/actions/build/vite/plugin.js +6 -14
- package/dist/actions/build/vite/plugin.js.map +1 -1
- package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js +24 -0
- package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js.map +1 -0
- package/dist/actions/build/vite/workbench-vite-plugins.js +8 -4
- package/dist/actions/build/vite/workbench-vite-plugins.js.map +1 -1
- package/dist/actions/deploy/checkBuiltOutput.js +25 -0
- package/dist/actions/deploy/checkBuiltOutput.js.map +1 -0
- package/dist/actions/deploy/deployInstallationConfig.js +91 -0
- package/dist/actions/deploy/deployInstallationConfig.js.map +1 -0
- package/dist/actions/deploy/getWorkbench.js +14 -25
- package/dist/actions/deploy/getWorkbench.js.map +1 -1
- package/dist/actions/deploy/viewDeployment.js +32 -0
- package/dist/actions/deploy/viewDeployment.js.map +1 -0
- package/dist/actions/dev/deriveInterfaces.js +47 -4
- package/dist/actions/dev/deriveInterfaces.js.map +1 -1
- package/dist/actions/dev/exposesSetId.js +69 -0
- package/dist/actions/dev/exposesSetId.js.map +1 -0
- package/dist/actions/dev/registry.js +17 -0
- package/dist/actions/dev/registry.js.map +1 -1
- package/dist/actions/dev/startDevManifestWatcher.js +2 -1
- package/dist/actions/dev/startDevManifestWatcher.js.map +1 -1
- package/dist/actions/dev/startDevServerRegistration.js +26 -11
- package/dist/actions/dev/startDevServerRegistration.js.map +1 -1
- package/dist/actions/dev/startWorkbenchDevServer.js +20 -5
- package/dist/actions/dev/startWorkbenchDevServer.js.map +1 -1
- package/dist/contract.js +33 -38
- package/dist/contract.js.map +1 -1
- package/dist/defineApp.js +55 -1
- package/dist/defineApp.js.map +1 -1
- package/dist/resolveWorkbenchApp.js +4 -1
- package/dist/resolveWorkbenchApp.js.map +1 -1
- package/package.json +7 -4
- package/dist/actions/dev/interfaceSetId.js +0 -51
- package/dist/actions/dev/interfaceSetId.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/build/artifact.ts"],"sourcesContent":["import {type
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/build/artifact.ts"],"sourcesContent":["import {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\nimport {installationConfigArtifacts} from './configs/artifact.js'\nimport {serviceArtifacts} from './services/artifact.js'\nimport {viewArtifacts} from './views/artifact.js'\n\n/**\n * What a {@link GeneratedArtifact.source} builder receives from the build — the\n * one thing it can't compute on its own, since only the build knows where the\n * runtime dir sits relative to the app's `src`.\n */\ninterface ArtifactContext {\n /** Import specifier for an app `src` file, relative to this artifact. */\n resolveImport: (src: string) => string\n}\n\n/**\n * One file the federation build generates into the runtime dir for a declared\n * interface. Each interface type — views, services — expands its declarations\n * into a flat list of these; the build then writes them and maps the ones the\n * host loads directly into the module-federation manifest.\n *\n * Adding an interface type is adding a builder that returns\n * `GeneratedArtifact[]` — the write loop and the expose mapping never change.\n */\nexport interface GeneratedArtifact {\n /** Path relative to the federation runtime dir, e.g. `views/feed/panel.js`. */\n path: string\n /** Build the file's contents. */\n source: (context: ArtifactContext) => string\n\n /**\n * Module-federation expose key when the host loads this artifact directly —\n * a view component `./views/feed/panel`, a service loader `./services/unread`.\n * Omitted for a file the host never loads on its own, like a worker bundle\n * (reached through its sibling loader).\n */\n expose?: string\n}\n\n/**\n * Map the artifacts the host loads directly (those with an `expose`) to their\n * runtime-dir paths, for the module-federation `exposes` field. `toExposePath`\n * turns a runtime-relative artifact path into the value federation wants — the\n * caller owns the runtime-dir location and any entry resolution.\n */\nexport function artifactExposes(\n artifacts: readonly GeneratedArtifact[],\n toExposePath: (artifactPath: string) => string,\n): Record<string, string> {\n const exposes: Record<string, string> = {}\n for (const artifact of artifacts) {\n if (artifact.expose) {\n exposes[artifact.expose] = toExposePath(artifact.path)\n }\n }\n return exposes\n}\n\n/**\n * Expand what the app exposes into the flat artifact set the federation build\n * writes — the single place that composes the per-type expanders, so the expose\n * mapping and the file writing read from one expansion.\n */\nexport function workbenchArtifacts(exposes: WorkbenchExposes): GeneratedArtifact[] {\n return [\n ...viewArtifacts(exposes.views ?? []),\n ...serviceArtifacts(exposes.services ?? []),\n ...installationConfigArtifacts(exposes.installationConfig),\n ]\n}\n"],"names":["installationConfigArtifacts","serviceArtifacts","viewArtifacts","artifactExposes","artifacts","toExposePath","exposes","artifact","expose","path","workbenchArtifacts","views","services","installationConfig"],"mappings":"AACA,SAAQA,2BAA2B,QAAO,wBAAuB;AACjE,SAAQC,gBAAgB,QAAO,yBAAwB;AACvD,SAAQC,aAAa,QAAO,sBAAqB;AAoCjD;;;;;CAKC,GACD,OAAO,SAASC,gBACdC,SAAuC,EACvCC,YAA8C;IAE9C,MAAMC,UAAkC,CAAC;IACzC,KAAK,MAAMC,YAAYH,UAAW;QAChC,IAAIG,SAASC,MAAM,EAAE;YACnBF,OAAO,CAACC,SAASC,MAAM,CAAC,GAAGH,aAAaE,SAASE,IAAI;QACvD;IACF;IACA,OAAOH;AACT;AAEA;;;;CAIC,GACD,OAAO,SAASI,mBAAmBJ,OAAyB;IAC1D,OAAO;WACFJ,cAAcI,QAAQK,KAAK,IAAI,EAAE;WACjCV,iBAAiBK,QAAQM,QAAQ,IAAI,EAAE;WACvCZ,4BAA4BM,QAAQO,kBAAkB;KAC1D;AACH"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { INSTALLATION_CONFIG_TYPE, MEDIA_LIBRARY_INSTALLATION_CONFIG_CONTRACT_VERSION } from '../../../contract.js';
|
|
2
|
+
const CONFIGS_DIR_NAME = 'configs';
|
|
3
|
+
/** Expand the installation config into one federation module aggregating all fields, so the host imports one live config value. */ export function installationConfigArtifacts(config) {
|
|
4
|
+
if (!config) return [];
|
|
5
|
+
return [
|
|
6
|
+
{
|
|
7
|
+
expose: `./${CONFIGS_DIR_NAME}/${INSTALLATION_CONFIG_TYPE}`,
|
|
8
|
+
path: `${CONFIGS_DIR_NAME}/${INSTALLATION_CONFIG_TYPE}.js`,
|
|
9
|
+
source: ({ resolveImport })=>mediaLibraryInstallationConfigSource({
|
|
10
|
+
config,
|
|
11
|
+
resolveImport
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Emits `export const config`: each field's serializable `{name, title, public}`
|
|
18
|
+
* plus its live `defineField` value, the one thing the wire can't carry.
|
|
19
|
+
* `appType` stays off the module — the host assigns it from the wire record.
|
|
20
|
+
*/ function mediaLibraryInstallationConfigSource(input) {
|
|
21
|
+
const { config, resolveImport } = input;
|
|
22
|
+
const imports = config.fields.map((field, index)=>`import field_${index} from ${JSON.stringify(resolveImport(field.src))}`).join('\n');
|
|
23
|
+
const entries = config.fields.map((field, index)=>` {name: ${JSON.stringify(field.name)}, title: ${JSON.stringify(field.title)}, ` + `public: ${JSON.stringify(Boolean(field.public))}, config: field_${index}},`).join('\n');
|
|
24
|
+
return `\
|
|
25
|
+
// This file is auto-generated on 'sanity build' / 'sanity dev'
|
|
26
|
+
// Modifications to this file are automatically discarded
|
|
27
|
+
${imports}
|
|
28
|
+
|
|
29
|
+
export const type = ${JSON.stringify(INSTALLATION_CONFIG_TYPE)}
|
|
30
|
+
export const version = ${MEDIA_LIBRARY_INSTALLATION_CONFIG_CONTRACT_VERSION}
|
|
31
|
+
export const config = {
|
|
32
|
+
fields: [
|
|
33
|
+
${entries}
|
|
34
|
+
],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (import.meta.hot) {
|
|
38
|
+
// A config is data, not a rendered island with a live root to remount —
|
|
39
|
+
// self-accept so a field edit swaps the module in place; the host re-reads it.
|
|
40
|
+
import.meta.hot.accept()
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=artifact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/build/configs/artifact.ts"],"sourcesContent":["import {\n INSTALLATION_CONFIG_TYPE,\n MEDIA_LIBRARY_INSTALLATION_CONFIG_CONTRACT_VERSION,\n} from '../../../contract.js'\nimport {type GeneratedArtifact} from '../artifact.js'\n\nconst CONFIGS_DIR_NAME = 'configs'\n\n/**\n * The installation config to generate a module for.\n * @internal\n */\nexport interface ConfigArtifact {\n fields: {name: string; public?: boolean; src: string; title: string}[]\n}\n\n/** Expand the installation config into one federation module aggregating all fields, so the host imports one live config value. */\nexport function installationConfigArtifacts(\n config: ConfigArtifact | undefined,\n): GeneratedArtifact[] {\n if (!config) return []\n return [\n {\n expose: `./${CONFIGS_DIR_NAME}/${INSTALLATION_CONFIG_TYPE}`,\n path: `${CONFIGS_DIR_NAME}/${INSTALLATION_CONFIG_TYPE}.js`,\n source: ({resolveImport}) => mediaLibraryInstallationConfigSource({config, resolveImport}),\n },\n ]\n}\n\n/**\n * Emits `export const config`: each field's serializable `{name, title, public}`\n * plus its live `defineField` value, the one thing the wire can't carry.\n * `appType` stays off the module — the host assigns it from the wire record.\n */\nfunction mediaLibraryInstallationConfigSource(input: {\n config: ConfigArtifact\n resolveImport: (src: string) => string\n}): string {\n const {config, resolveImport} = input\n const imports = config.fields\n .map((field, index) => `import field_${index} from ${JSON.stringify(resolveImport(field.src))}`)\n .join('\\n')\n const entries = config.fields\n .map(\n (field, index) =>\n ` {name: ${JSON.stringify(field.name)}, title: ${JSON.stringify(field.title)}, ` +\n `public: ${JSON.stringify(Boolean(field.public))}, config: field_${index}},`,\n )\n .join('\\n')\n return `\\\n// This file is auto-generated on 'sanity build' / 'sanity dev'\n// Modifications to this file are automatically discarded\n${imports}\n\nexport const type = ${JSON.stringify(INSTALLATION_CONFIG_TYPE)}\nexport const version = ${MEDIA_LIBRARY_INSTALLATION_CONFIG_CONTRACT_VERSION}\nexport const config = {\n fields: [\n${entries}\n ],\n}\n\nif (import.meta.hot) {\n // A config is data, not a rendered island with a live root to remount —\n // self-accept so a field edit swaps the module in place; the host re-reads it.\n import.meta.hot.accept()\n}\n`\n}\n"],"names":["INSTALLATION_CONFIG_TYPE","MEDIA_LIBRARY_INSTALLATION_CONFIG_CONTRACT_VERSION","CONFIGS_DIR_NAME","installationConfigArtifacts","config","expose","path","source","resolveImport","mediaLibraryInstallationConfigSource","input","imports","fields","map","field","index","JSON","stringify","src","join","entries","name","title","Boolean","public"],"mappings":"AAAA,SACEA,wBAAwB,EACxBC,kDAAkD,QAC7C,uBAAsB;AAG7B,MAAMC,mBAAmB;AAUzB,iIAAiI,GACjI,OAAO,SAASC,4BACdC,MAAkC;IAElC,IAAI,CAACA,QAAQ,OAAO,EAAE;IACtB,OAAO;QACL;YACEC,QAAQ,CAAC,EAAE,EAAEH,iBAAiB,CAAC,EAAEF,0BAA0B;YAC3DM,MAAM,GAAGJ,iBAAiB,CAAC,EAAEF,yBAAyB,GAAG,CAAC;YAC1DO,QAAQ,CAAC,EAACC,aAAa,EAAC,GAAKC,qCAAqC;oBAACL;oBAAQI;gBAAa;QAC1F;KACD;AACH;AAEA;;;;CAIC,GACD,SAASC,qCAAqCC,KAG7C;IACC,MAAM,EAACN,MAAM,EAAEI,aAAa,EAAC,GAAGE;IAChC,MAAMC,UAAUP,OAAOQ,MAAM,CAC1BC,GAAG,CAAC,CAACC,OAAOC,QAAU,CAAC,aAAa,EAAEA,MAAM,MAAM,EAAEC,KAAKC,SAAS,CAACT,cAAcM,MAAMI,GAAG,IAAI,EAC9FC,IAAI,CAAC;IACR,MAAMC,UAAUhB,OAAOQ,MAAM,CAC1BC,GAAG,CACF,CAACC,OAAOC,QACN,CAAC,WAAW,EAAEC,KAAKC,SAAS,CAACH,MAAMO,IAAI,EAAE,SAAS,EAAEL,KAAKC,SAAS,CAACH,MAAMQ,KAAK,EAAE,EAAE,CAAC,GACnF,CAAC,QAAQ,EAAEN,KAAKC,SAAS,CAACM,QAAQT,MAAMU,MAAM,GAAG,gBAAgB,EAAET,MAAM,EAAE,CAAC,EAE/EI,IAAI,CAAC;IACR,OAAO,CAAC;;;AAGV,EAAER,QAAQ;;oBAEU,EAAEK,KAAKC,SAAS,CAACjB,0BAA0B;uBACxC,EAAEC,mDAAmD;;;AAG5E,EAAEmB,QAAQ;;;;;;;;;AASV,CAAC;AACD"}
|
|
@@ -8,7 +8,7 @@ import { sanityFederationRuntime } from './plugins/plugin-sanity-federation-runt
|
|
|
8
8
|
/**
|
|
9
9
|
* @internal
|
|
10
10
|
*/ export const federation = (options)=>{
|
|
11
|
-
const { exposes
|
|
11
|
+
const { exposes, name: defaultName, pkgJson, workDir = process.cwd() } = options;
|
|
12
12
|
let name = defaultName;
|
|
13
13
|
if (!name) {
|
|
14
14
|
name = pkgJson?.name;
|
|
@@ -25,29 +25,21 @@ import { sanityFederationRuntime } from './plugins/plugin-sanity-federation-runt
|
|
|
25
25
|
return resolvedPath;
|
|
26
26
|
}
|
|
27
27
|
const entryPath = resolveEntryPath(generatedEntry);
|
|
28
|
-
const resolvedExposes = {};
|
|
29
|
-
for (const [key, exposePath] of Object.entries(defaultExposes)){
|
|
30
|
-
resolvedExposes[key] = resolveEntryPath(exposePath) ?? exposePath;
|
|
31
|
-
}
|
|
32
28
|
// Each view component (`./views/<view>/<component>`) and each service loader
|
|
33
29
|
// (`./services/<name>`) is exposed straight to the host, pointing at the file
|
|
34
30
|
// the extension-artifacts plugin generates under RUNTIME_DIR. A service's
|
|
35
31
|
// worker bundle carries no expose — the host reaches it through its loader.
|
|
36
|
-
const artifacts = workbenchArtifacts({
|
|
37
|
-
|
|
38
|
-
views
|
|
39
|
-
});
|
|
40
|
-
const interfaceExposes = artifactExposes(artifacts, (artifactPath)=>resolveEntryPath(`./${RUNTIME_DIR}/${artifactPath}`));
|
|
32
|
+
const artifacts = workbenchArtifacts(exposes ?? {});
|
|
33
|
+
const artifactModuleExposes = artifactExposes(artifacts, (artifactPath)=>resolveEntryPath(`./${RUNTIME_DIR}/${artifactPath}`));
|
|
41
34
|
// A dock-only app (`isApp` with no `appEntry`) has no navigable full-page
|
|
42
35
|
// view, so it exposes no `./App` — only its views. Studios and apps with an
|
|
43
36
|
// entry expose `./App` (the generated render entry).
|
|
44
37
|
const exposesApp = !options.isApp || options.appEntry !== undefined;
|
|
45
|
-
const
|
|
38
|
+
const federationExposes = {
|
|
46
39
|
...exposesApp ? {
|
|
47
40
|
'./App': entryPath
|
|
48
41
|
} : {},
|
|
49
|
-
...
|
|
50
|
-
...interfaceExposes
|
|
42
|
+
...artifactModuleExposes
|
|
51
43
|
};
|
|
52
44
|
const runtimeOptions = options.isApp ? {
|
|
53
45
|
appEntry: options.appEntry,
|
|
@@ -65,7 +57,7 @@ import { sanityFederationRuntime } from './plugins/plugin-sanity-federation-runt
|
|
|
65
57
|
artifacts
|
|
66
58
|
}),
|
|
67
59
|
sanityModuleFederation({
|
|
68
|
-
exposes,
|
|
60
|
+
exposes: federationExposes,
|
|
69
61
|
name
|
|
70
62
|
})
|
|
71
63
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/build/vite/plugin.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type ModuleFederationOptions} from '@module-federation/vite'\nimport {type PackageJson} from '@sanity/cli-core'\nimport {type PluginOption} from 'vite'\n\nimport {type
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/build/vite/plugin.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type ModuleFederationOptions} from '@module-federation/vite'\nimport {type PackageJson} from '@sanity/cli-core'\nimport {type PluginOption} from 'vite'\n\nimport {type WorkbenchExposes} from '../../../resolveWorkbenchApp.js'\nimport {artifactExposes, workbenchArtifacts} from '../artifact.js'\nimport {FEDERATION_FILE_NAME, RUNTIME_DIR} from './constants.js'\nimport {type FederationOptions, sanityModuleFederation} from './plugins/plugin-module-federation.js'\nimport {sanityEnvironmentPlugin} from './plugins/plugin-sanity-environment.js'\nimport {sanityExtensionArtifacts} from './plugins/plugin-sanity-extension-artifacts.js'\nimport {\n type FederationRuntimeOptions,\n sanityFederationRuntime,\n} from './plugins/plugin-sanity-federation-runtime.js'\n\ninterface FederationPluginOptionsBase extends Omit<Partial<FederationOptions>, 'exposes'> {\n exposes?: WorkbenchExposes\n pkgJson?: PackageJson\n /**\n * Current working directory to read package.json from, defaults to process.cwd()\n */\n workDir?: string\n}\n\ninterface AppFederationPluginOptions extends FederationPluginOptionsBase {\n isApp: true\n\n /**\n * Relative path to the App entry from the runtime directory, e.g.\n * `../../src/App.tsx`. Omit it for a dock-only app that declares no `entry`:\n * no `./App` is exposed and the remote serves only its views.\n */\n appEntry?: string\n studioConfigPath?: never\n}\n\ninterface StudioFederationPluginOptions extends FederationPluginOptionsBase {\n /** relative path to the Studio config file from the runtime directory (e.g. `../../sanity.config.ts`). */\n studioConfigPath: string\n\n appEntry?: never\n /** @defaultValue false */\n isApp?: false\n}\n\n/**\n * Plugin options for the federation vite plugin.\n *\n * Discriminated on `isApp`:\n * - `isApp: true` → requires `appEntry`\n * - `isApp: false` (default) → requires `studioConfigPath`\n *\n * @internal\n */\ntype FederationPluginOptions = AppFederationPluginOptions | StudioFederationPluginOptions\n\n/**\n * @internal\n */\nexport const federation = (options: FederationPluginOptions): PluginOption => {\n const {exposes, name: defaultName, pkgJson, workDir = process.cwd()} = options\n\n let name = defaultName\n\n if (!name) {\n name = pkgJson?.name\n }\n\n if (!name) {\n throw new Error('\"name\" option is required but could not be inferred from package.json')\n }\n\n const generatedEntry = `./${RUNTIME_DIR}/${FEDERATION_FILE_NAME}.jsx`\n\n function resolveEntryPath(entry: string) {\n const resolvedPath = path.resolve(workDir, entry)\n\n if (!resolvedPath) {\n throw new Error(\n `Could not resolve path for entry \"${entry}\". Please check that the file exists and the path is correct.`,\n )\n }\n\n return resolvedPath\n }\n\n const entryPath = resolveEntryPath(generatedEntry)\n\n // Each view component (`./views/<view>/<component>`) and each service loader\n // (`./services/<name>`) is exposed straight to the host, pointing at the file\n // the extension-artifacts plugin generates under RUNTIME_DIR. A service's\n // worker bundle carries no expose — the host reaches it through its loader.\n const artifacts = workbenchArtifacts(exposes ?? {})\n const artifactModuleExposes = artifactExposes(artifacts, (artifactPath) =>\n resolveEntryPath(`./${RUNTIME_DIR}/${artifactPath}`),\n )\n\n // A dock-only app (`isApp` with no `appEntry`) has no navigable full-page\n // view, so it exposes no `./App` — only its views. Studios and apps with an\n // entry expose `./App` (the generated render entry).\n const exposesApp = !options.isApp || options.appEntry !== undefined\n\n const federationExposes: NonNullable<ModuleFederationOptions['exposes']> = {\n ...(exposesApp ? {'./App': entryPath} : {}),\n ...artifactModuleExposes,\n }\n\n const runtimeOptions: FederationRuntimeOptions = options.isApp\n ? {appEntry: options.appEntry, isApp: true}\n : {isApp: false, studioConfigPath: options.studioConfigPath}\n\n return [\n sanityEnvironmentPlugin({input: entryPath}),\n sanityFederationRuntime(runtimeOptions),\n sanityExtensionArtifacts({artifacts}),\n sanityModuleFederation({exposes: federationExposes, name}),\n ]\n}\n"],"names":["path","artifactExposes","workbenchArtifacts","FEDERATION_FILE_NAME","RUNTIME_DIR","sanityModuleFederation","sanityEnvironmentPlugin","sanityExtensionArtifacts","sanityFederationRuntime","federation","options","exposes","name","defaultName","pkgJson","workDir","process","cwd","Error","generatedEntry","resolveEntryPath","entry","resolvedPath","resolve","entryPath","artifacts","artifactModuleExposes","artifactPath","exposesApp","isApp","appEntry","undefined","federationExposes","runtimeOptions","studioConfigPath","input"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAO5B,SAAQC,eAAe,EAAEC,kBAAkB,QAAO,iBAAgB;AAClE,SAAQC,oBAAoB,EAAEC,WAAW,QAAO,iBAAgB;AAChE,SAAgCC,sBAAsB,QAAO,wCAAuC;AACpG,SAAQC,uBAAuB,QAAO,yCAAwC;AAC9E,SAAQC,wBAAwB,QAAO,iDAAgD;AACvF,SAEEC,uBAAuB,QAClB,gDAA+C;AA2CtD;;CAEC,GACD,OAAO,MAAMC,aAAa,CAACC;IACzB,MAAM,EAACC,OAAO,EAAEC,MAAMC,WAAW,EAAEC,OAAO,EAAEC,UAAUC,QAAQC,GAAG,EAAE,EAAC,GAAGP;IAEvE,IAAIE,OAAOC;IAEX,IAAI,CAACD,MAAM;QACTA,OAAOE,SAASF;IAClB;IAEA,IAAI,CAACA,MAAM;QACT,MAAM,IAAIM,MAAM;IAClB;IAEA,MAAMC,iBAAiB,CAAC,EAAE,EAAEf,YAAY,CAAC,EAAED,qBAAqB,IAAI,CAAC;IAErE,SAASiB,iBAAiBC,KAAa;QACrC,MAAMC,eAAetB,KAAKuB,OAAO,CAACR,SAASM;QAE3C,IAAI,CAACC,cAAc;YACjB,MAAM,IAAIJ,MACR,CAAC,kCAAkC,EAAEG,MAAM,6DAA6D,CAAC;QAE7G;QAEA,OAAOC;IACT;IAEA,MAAME,YAAYJ,iBAAiBD;IAEnC,6EAA6E;IAC7E,8EAA8E;IAC9E,0EAA0E;IAC1E,4EAA4E;IAC5E,MAAMM,YAAYvB,mBAAmBS,WAAW,CAAC;IACjD,MAAMe,wBAAwBzB,gBAAgBwB,WAAW,CAACE,eACxDP,iBAAiB,CAAC,EAAE,EAAEhB,YAAY,CAAC,EAAEuB,cAAc;IAGrD,0EAA0E;IAC1E,4EAA4E;IAC5E,qDAAqD;IACrD,MAAMC,aAAa,CAAClB,QAAQmB,KAAK,IAAInB,QAAQoB,QAAQ,KAAKC;IAE1D,MAAMC,oBAAqE;QACzE,GAAIJ,aAAa;YAAC,SAASJ;QAAS,IAAI,CAAC,CAAC;QAC1C,GAAGE,qBAAqB;IAC1B;IAEA,MAAMO,iBAA2CvB,QAAQmB,KAAK,GAC1D;QAACC,UAAUpB,QAAQoB,QAAQ;QAAED,OAAO;IAAI,IACxC;QAACA,OAAO;QAAOK,kBAAkBxB,QAAQwB,gBAAgB;IAAA;IAE7D,OAAO;QACL5B,wBAAwB;YAAC6B,OAAOX;QAAS;QACzChB,wBAAwByB;QACxB1B,yBAAyB;YAACkB;QAAS;QACnCpB,uBAAuB;YAACM,SAASqB;YAAmBpB;QAAI;KACzD;AACH,EAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bake the app's bus identity into its bundle: `@sanity/runtime` reads
|
|
3
|
+
* `__SANITY_APP_ID__` where it connects. `define` covers everything the
|
|
4
|
+
* pipeline transforms (all of a production build, and dev-served source); the
|
|
5
|
+
* esbuild define covers dev's pre-bundled dependencies, which skip Vite's
|
|
6
|
+
* define transform.
|
|
7
|
+
*/ export function sanityAppId(appId) {
|
|
8
|
+
const define = {
|
|
9
|
+
__SANITY_APP_ID__: JSON.stringify(appId)
|
|
10
|
+
};
|
|
11
|
+
return {
|
|
12
|
+
config: ()=>({
|
|
13
|
+
define,
|
|
14
|
+
optimizeDeps: {
|
|
15
|
+
esbuildOptions: {
|
|
16
|
+
define
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
20
|
+
name: 'sanity/workbench/app-id'
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=plugin-sanity-app-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/actions/build/vite/plugins/plugin-sanity-app-id.ts"],"sourcesContent":["import {type Plugin} from 'vite'\n\n/**\n * Bake the app's bus identity into its bundle: `@sanity/runtime` reads\n * `__SANITY_APP_ID__` where it connects. `define` covers everything the\n * pipeline transforms (all of a production build, and dev-served source); the\n * esbuild define covers dev's pre-bundled dependencies, which skip Vite's\n * define transform.\n */\nexport function sanityAppId(appId: string): Plugin {\n const define = {__SANITY_APP_ID__: JSON.stringify(appId)}\n return {\n config: () => ({define, optimizeDeps: {esbuildOptions: {define}}}),\n name: 'sanity/workbench/app-id',\n }\n}\n"],"names":["sanityAppId","appId","define","__SANITY_APP_ID__","JSON","stringify","config","optimizeDeps","esbuildOptions","name"],"mappings":"AAEA;;;;;;CAMC,GACD,OAAO,SAASA,YAAYC,KAAa;IACvC,MAAMC,SAAS;QAACC,mBAAmBC,KAAKC,SAAS,CAACJ;IAAM;IACxD,OAAO;QACLK,QAAQ,IAAO,CAAA;gBAACJ;gBAAQK,cAAc;oBAACC,gBAAgB;wBAACN;oBAAM;gBAAC;YAAC,CAAA;QAChEO,MAAM;IACR;AACF"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { readPackageJson } from '@sanity/cli-core';
|
|
9
9
|
import { federation } from './plugin.js';
|
|
10
|
+
import { sanityAppId } from './plugins/plugin-sanity-app-id.js';
|
|
10
11
|
/**
|
|
11
12
|
* A workbench studio renders from its `sanity.config.*`, so the build needs one.
|
|
12
13
|
* An explicit `applicationType: 'studio'` wins over detection, so a studio can
|
|
@@ -19,9 +20,9 @@ import { federation } from './plugin.js';
|
|
|
19
20
|
return relativeConfigLocation;
|
|
20
21
|
}
|
|
21
22
|
/** Build the Vite plugins for a workbench app's module-federation remote. */ export async function workbenchVitePlugins(options) {
|
|
22
|
-
const { cwd, entries,
|
|
23
|
+
const { appId, cwd, entries, exposes, isApp } = options;
|
|
23
24
|
const pkgJson = await readPackageJson(path.join(cwd, 'package.json'));
|
|
24
|
-
|
|
25
|
+
const federationPlugin = federation({
|
|
25
26
|
...isApp ? {
|
|
26
27
|
// `null` relativeEntry (a branded app with no `entry`) → omit `appEntry`,
|
|
27
28
|
// so the remote exposes no `./App`, only its views.
|
|
@@ -33,11 +34,14 @@ import { federation } from './plugin.js';
|
|
|
33
34
|
isApp: false,
|
|
34
35
|
studioConfigPath: requireStudioConfigPath(entries.relativeConfigLocation)
|
|
35
36
|
},
|
|
37
|
+
exposes,
|
|
36
38
|
pkgJson,
|
|
37
|
-
services,
|
|
38
|
-
views,
|
|
39
39
|
workDir: cwd
|
|
40
40
|
});
|
|
41
|
+
return appId === undefined ? federationPlugin : [
|
|
42
|
+
federationPlugin,
|
|
43
|
+
sanityAppId(appId)
|
|
44
|
+
];
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
//# sourceMappingURL=workbench-vite-plugins.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/build/vite/workbench-vite-plugins.ts"],"sourcesContent":["// The build-facing entry of the workbench federation stack: turn a workbench\n// app's build inputs into the Vite plugins that produce its module-federation\n// remote. `@sanity/cli-build`'s `getViteConfig` calls this instead of\n// assembling `federation`'s options itself, so the discriminated app-vs-studio\n// option shape, the no-app-view rule, and the studio-config requirement all\n// live here next to `federation` — the build package just hands over its inputs.\n\nimport path from 'node:path'\n\nimport {readPackageJson} from '@sanity/cli-core'\nimport {type PluginOption} from 'vite'\n\nimport {type
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/build/vite/workbench-vite-plugins.ts"],"sourcesContent":["// The build-facing entry of the workbench federation stack: turn a workbench\n// app's build inputs into the Vite plugins that produce its module-federation\n// remote. `@sanity/cli-build`'s `getViteConfig` calls this instead of\n// assembling `federation`'s options itself, so the discriminated app-vs-studio\n// option shape, the no-app-view rule, and the studio-config requirement all\n// live here next to `federation` — the build package just hands over its inputs.\n\nimport path from 'node:path'\n\nimport {readPackageJson} from '@sanity/cli-core'\nimport {type PluginOption} from 'vite'\n\nimport {type WorkbenchExposes} from '../../../resolveWorkbenchApp.js'\nimport {federation} from './plugin.js'\nimport {sanityAppId} from './plugins/plugin-sanity-app-id.js'\n\ninterface WorkbenchViteOptions {\n /** Project root — read for the federation remote name, and the plugin workDir. */\n cwd: string\n /**\n * Build entry paths relative to the federation runtime dir. `relativeEntry` is\n * the app's `entry` (null for a dock-only app with no app view);\n * `relativeConfigLocation` is the studio's `sanity.config.*` (null when absent).\n */\n entries: {relativeConfigLocation: string | null; relativeEntry: string | null}\n\n /** The app's bus identity, stamped into its modules for `@sanity/runtime`. */\n appId?: string\n\n exposes?: WorkbenchExposes\n /** App (vs studio) build — selects the discriminated federation option shape. */\n isApp?: boolean\n}\n\n/**\n * A workbench studio renders from its `sanity.config.*`, so the build needs one.\n * An explicit `applicationType: 'studio'` wins over detection, so a studio can\n * reach here with no config file — fail with the fix rather than a cryptic build\n * error downstream.\n */\nfunction requireStudioConfigPath(relativeConfigLocation: string | null): string {\n if (relativeConfigLocation === null) {\n throw new Error(\n 'Workbench studios need a sanity.config.js or sanity.config.ts file. ' +\n \"Add one, or remove `applicationType: 'studio'` from `unstable_defineApp` \" +\n 'to let the CLI infer the application type.',\n )\n }\n return relativeConfigLocation\n}\n\n/** Build the Vite plugins for a workbench app's module-federation remote. */\nexport async function workbenchVitePlugins(options: WorkbenchViteOptions): Promise<PluginOption> {\n const {appId, cwd, entries, exposes, isApp} = options\n const pkgJson = await readPackageJson(path.join(cwd, 'package.json'))\n\n const federationPlugin = federation({\n ...(isApp\n ? {\n // `null` relativeEntry (a branded app with no `entry`) → omit `appEntry`,\n // so the remote exposes no `./App`, only its views.\n ...(entries.relativeEntry ? {appEntry: entries.relativeEntry} : {}),\n isApp: true as const,\n }\n : {\n isApp: false as const,\n studioConfigPath: requireStudioConfigPath(entries.relativeConfigLocation),\n }),\n exposes,\n pkgJson,\n workDir: cwd,\n })\n\n return appId === undefined ? federationPlugin : [federationPlugin, sanityAppId(appId)]\n}\n"],"names":["path","readPackageJson","federation","sanityAppId","requireStudioConfigPath","relativeConfigLocation","Error","workbenchVitePlugins","options","appId","cwd","entries","exposes","isApp","pkgJson","join","federationPlugin","relativeEntry","appEntry","studioConfigPath","workDir","undefined"],"mappings":"AAAA,6EAA6E;AAC7E,8EAA8E;AAC9E,sEAAsE;AACtE,+EAA+E;AAC/E,4EAA4E;AAC5E,iFAAiF;AAEjF,OAAOA,UAAU,YAAW;AAE5B,SAAQC,eAAe,QAAO,mBAAkB;AAIhD,SAAQC,UAAU,QAAO,cAAa;AACtC,SAAQC,WAAW,QAAO,oCAAmC;AAoB7D;;;;;CAKC,GACD,SAASC,wBAAwBC,sBAAqC;IACpE,IAAIA,2BAA2B,MAAM;QACnC,MAAM,IAAIC,MACR,yEACE,8EACA;IAEN;IACA,OAAOD;AACT;AAEA,2EAA2E,GAC3E,OAAO,eAAeE,qBAAqBC,OAA6B;IACtE,MAAM,EAACC,KAAK,EAAEC,GAAG,EAAEC,OAAO,EAAEC,OAAO,EAAEC,KAAK,EAAC,GAAGL;IAC9C,MAAMM,UAAU,MAAMb,gBAAgBD,KAAKe,IAAI,CAACL,KAAK;IAErD,MAAMM,mBAAmBd,WAAW;QAClC,GAAIW,QACA;YACE,0EAA0E;YAC1E,oDAAoD;YACpD,GAAIF,QAAQM,aAAa,GAAG;gBAACC,UAAUP,QAAQM,aAAa;YAAA,IAAI,CAAC,CAAC;YAClEJ,OAAO;QACT,IACA;YACEA,OAAO;YACPM,kBAAkBf,wBAAwBO,QAAQN,sBAAsB;QAC1E,CAAC;QACLO;QACAE;QACAM,SAASV;IACX;IAEA,OAAOD,UAAUY,YAAYL,mBAAmB;QAACA;QAAkBb,YAAYM;KAAO;AACxF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Throws unless `sourceDir` is a directory holding a federation build.
|
|
5
|
+
* Workbench builds emit a module-federation remote instead of a static SPA,
|
|
6
|
+
* so the usual `index.html` contract doesn't apply — `mf-manifest.json` is the
|
|
7
|
+
* marker that `sanity build` produced a federation build.
|
|
8
|
+
*/ export async function checkBuiltOutput(sourceDir) {
|
|
9
|
+
try {
|
|
10
|
+
const stats = await stat(sourceDir);
|
|
11
|
+
if (!stats.isDirectory()) {
|
|
12
|
+
throw new Error(`"${sourceDir}" is not a directory`);
|
|
13
|
+
}
|
|
14
|
+
} catch (err) {
|
|
15
|
+
throw err.code === 'ENOENT' ? new Error(`Directory "${sourceDir}" does not exist`) : err;
|
|
16
|
+
}
|
|
17
|
+
const manifestPath = join(sourceDir, 'mf-manifest.json');
|
|
18
|
+
try {
|
|
19
|
+
await stat(manifestPath);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
throw err.code === 'ENOENT' ? new Error(`"${manifestPath}" does not exist. ` + 'The deploy directory must contain a federation build created with "sanity build".') : err;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=checkBuiltOutput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/checkBuiltOutput.ts"],"sourcesContent":["import {stat} from 'node:fs/promises'\nimport {join} from 'node:path'\n\n/**\n * Throws unless `sourceDir` is a directory holding a federation build.\n * Workbench builds emit a module-federation remote instead of a static SPA,\n * so the usual `index.html` contract doesn't apply — `mf-manifest.json` is the\n * marker that `sanity build` produced a federation build.\n */\nexport async function checkBuiltOutput(sourceDir: string): Promise<void> {\n try {\n const stats = await stat(sourceDir)\n if (!stats.isDirectory()) {\n throw new Error(`\"${sourceDir}\" is not a directory`)\n }\n } catch (err) {\n throw err.code === 'ENOENT' ? new Error(`Directory \"${sourceDir}\" does not exist`) : err\n }\n\n const manifestPath = join(sourceDir, 'mf-manifest.json')\n try {\n await stat(manifestPath)\n } catch (err) {\n throw err.code === 'ENOENT'\n ? new Error(\n `\"${manifestPath}\" does not exist. ` +\n 'The deploy directory must contain a federation build created with \"sanity build\".',\n )\n : err\n }\n}\n"],"names":["stat","join","checkBuiltOutput","sourceDir","stats","isDirectory","Error","err","code","manifestPath"],"mappings":"AAAA,SAAQA,IAAI,QAAO,mBAAkB;AACrC,SAAQC,IAAI,QAAO,YAAW;AAE9B;;;;;CAKC,GACD,OAAO,eAAeC,iBAAiBC,SAAiB;IACtD,IAAI;QACF,MAAMC,QAAQ,MAAMJ,KAAKG;QACzB,IAAI,CAACC,MAAMC,WAAW,IAAI;YACxB,MAAM,IAAIC,MAAM,CAAC,CAAC,EAAEH,UAAU,oBAAoB,CAAC;QACrD;IACF,EAAE,OAAOI,KAAK;QACZ,MAAMA,IAAIC,IAAI,KAAK,WAAW,IAAIF,MAAM,CAAC,WAAW,EAAEH,UAAU,gBAAgB,CAAC,IAAII;IACvF;IAEA,MAAME,eAAeR,KAAKE,WAAW;IACrC,IAAI;QACF,MAAMH,KAAKS;IACb,EAAE,OAAOF,KAAK;QACZ,MAAMA,IAAIC,IAAI,KAAK,WACf,IAAIF,MACF,CAAC,CAAC,EAAEG,aAAa,kBAAkB,CAAC,GAClC,uFAEJF;IACN;AACF"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { basename, dirname } from 'node:path';
|
|
2
|
+
import { PassThrough } from 'node:stream';
|
|
3
|
+
import { styleText } from 'node:util';
|
|
4
|
+
import { createGzip } from 'node:zlib';
|
|
5
|
+
import { getGlobalCliClient, subdebug } from '@sanity/cli-core';
|
|
6
|
+
import FormData from 'form-data';
|
|
7
|
+
import { pack } from 'tar-fs';
|
|
8
|
+
// Brett's app-registry endpoints live behind the experimental `vX` version.
|
|
9
|
+
const INSTALLATIONS_API_VERSION = 'vX';
|
|
10
|
+
const debug = subdebug('deploy');
|
|
11
|
+
/**
|
|
12
|
+
* The org's active installation for an app type, or `undefined` when none is
|
|
13
|
+
* installed. Read-only, so `--dry-run` can report deployability.
|
|
14
|
+
* @internal
|
|
15
|
+
*/ export async function resolveInstallationId(options) {
|
|
16
|
+
switch(options.appType){
|
|
17
|
+
case 'media-library':
|
|
18
|
+
{
|
|
19
|
+
return resolveSingletonInstallationId(options.organizationId, 'media-library');
|
|
20
|
+
}
|
|
21
|
+
default:
|
|
22
|
+
{
|
|
23
|
+
throw new Error(`Cannot create installation config for unknown app type: ${options.appType}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A report heading and item list for a config; a media library's `fields` are
|
|
29
|
+
* one of potentially many shapes.
|
|
30
|
+
* @internal
|
|
31
|
+
*/ export function summarizeInstallationConfig(config) {
|
|
32
|
+
switch(config.appType){
|
|
33
|
+
case 'media-library':
|
|
34
|
+
{
|
|
35
|
+
const items = config.fields.map((field)=>` ${field.title} (${field.name})`).join('\n');
|
|
36
|
+
return `Media library fields:\n${items}`;
|
|
37
|
+
}
|
|
38
|
+
default:
|
|
39
|
+
{
|
|
40
|
+
throw new Error(`Cannot create installation config for unknown app type: ${config.appType}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Upload the built module-federation remote to the installation as its config
|
|
46
|
+
* snapshot. `installationId` is resolved by the caller so `--dry-run` never
|
|
47
|
+
* reaches this mutating step.
|
|
48
|
+
* @internal
|
|
49
|
+
*/ export async function deployInstallationConfig(options) {
|
|
50
|
+
const { appType, installationId, output, sourceDir, version } = options;
|
|
51
|
+
const tarball = pack(dirname(sourceDir), {
|
|
52
|
+
entries: [
|
|
53
|
+
basename(sourceDir)
|
|
54
|
+
]
|
|
55
|
+
}).pipe(createGzip());
|
|
56
|
+
const formData = new FormData();
|
|
57
|
+
formData.append('version', version);
|
|
58
|
+
formData.append('tarball', tarball, {
|
|
59
|
+
contentType: 'application/gzip',
|
|
60
|
+
filename: 'installation-config.tar.gz'
|
|
61
|
+
});
|
|
62
|
+
const client = await getGlobalCliClient({
|
|
63
|
+
apiVersion: INSTALLATIONS_API_VERSION,
|
|
64
|
+
requireUser: true
|
|
65
|
+
});
|
|
66
|
+
await client.request({
|
|
67
|
+
body: formData.pipe(new PassThrough()),
|
|
68
|
+
headers: formData.getHeaders(),
|
|
69
|
+
method: 'POST',
|
|
70
|
+
uri: `/installations/${installationId}/configs`
|
|
71
|
+
});
|
|
72
|
+
debug('Deployed installation config for app type: %s', appType);
|
|
73
|
+
output.log(`\n🚀 ${styleText('bold', 'Success!')} Installation config deployed`);
|
|
74
|
+
}
|
|
75
|
+
/** The org's active singleton installation, matched on its slug. */ async function resolveSingletonInstallationId(organizationId, slug) {
|
|
76
|
+
const client = await getGlobalCliClient({
|
|
77
|
+
apiVersion: INSTALLATIONS_API_VERSION,
|
|
78
|
+
requireUser: true
|
|
79
|
+
});
|
|
80
|
+
// `limit=none` returns every installation in one response, no pagination.
|
|
81
|
+
const { data } = await client.request({
|
|
82
|
+
query: {
|
|
83
|
+
limit: 'none',
|
|
84
|
+
organizationId
|
|
85
|
+
},
|
|
86
|
+
uri: '/installations'
|
|
87
|
+
});
|
|
88
|
+
return data.find((item)=>item.application?.slug === slug)?.id;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=deployInstallationConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/deployInstallationConfig.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {PassThrough} from 'node:stream'\nimport {styleText} from 'node:util'\nimport {createGzip} from 'node:zlib'\n\nimport {getGlobalCliClient, type Output, subdebug} from '@sanity/cli-core'\nimport FormData from 'form-data'\nimport {pack} from 'tar-fs'\n\n// Brett's app-registry endpoints live behind the experimental `vX` version.\nconst INSTALLATIONS_API_VERSION = 'vX'\n\nconst debug = subdebug('deploy')\n\ninterface InstallationListItem {\n id: string\n\n application?: {slug?: string}\n}\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 installation 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 summarizeInstallationConfig(config: {\n appType: string\n fields: {name: string; title: string}[]\n}): string {\n switch (config.appType) {\n case 'media-library': {\n const items = config.fields.map((field) => ` ${field.title} (${field.name})`).join('\\n')\n return `Media library fields:\\n${items}`\n }\n default: {\n throw new Error(`Cannot create installation 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 deployInstallationConfig(options: {\n appType: string\n installationId: string\n output: Output\n sourceDir: string\n version: string\n}): Promise<void> {\n const {appType, installationId, output, sourceDir, version} = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n const formData = new FormData()\n formData.append('version', version)\n formData.append('tarball', tarball, {\n contentType: 'application/gzip',\n filename: 'installation-config.tar.gz',\n })\n\n const client = await getGlobalCliClient({\n apiVersion: INSTALLATIONS_API_VERSION,\n requireUser: true,\n })\n await client.request({\n body: formData.pipe(new PassThrough()),\n headers: formData.getHeaders(),\n method: 'POST',\n uri: `/installations/${installationId}/configs`,\n })\n\n debug('Deployed installation config for app type: %s', appType)\n output.log(`\\n🚀 ${styleText('bold', 'Success!')} Installation config deployed`)\n}\n\n/** The org's active singleton installation, matched on its slug. */\nasync function resolveSingletonInstallationId(\n organizationId: string,\n slug: string,\n): Promise<string | undefined> {\n const client = await getGlobalCliClient({\n apiVersion: INSTALLATIONS_API_VERSION,\n requireUser: true,\n })\n // `limit=none` returns every installation in one response, no pagination.\n const {data}: {data: InstallationListItem[]} = await client.request({\n query: {limit: 'none', organizationId},\n uri: '/installations',\n })\n return data.find((item) => item.application?.slug === slug)?.id\n}\n"],"names":["basename","dirname","PassThrough","styleText","createGzip","getGlobalCliClient","subdebug","FormData","pack","INSTALLATIONS_API_VERSION","debug","resolveInstallationId","options","appType","resolveSingletonInstallationId","organizationId","Error","summarizeInstallationConfig","config","items","fields","map","field","title","name","join","deployInstallationConfig","installationId","output","sourceDir","version","tarball","entries","pipe","formData","append","contentType","filename","client","apiVersion","requireUser","request","body","headers","getHeaders","method","uri","log","slug","data","query","limit","find","item","application","id"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAC3C,SAAQC,WAAW,QAAO,cAAa;AACvC,SAAQC,SAAS,QAAO,YAAW;AACnC,SAAQC,UAAU,QAAO,YAAW;AAEpC,SAAQC,kBAAkB,EAAeC,QAAQ,QAAO,mBAAkB;AAC1E,OAAOC,cAAc,YAAW;AAChC,SAAQC,IAAI,QAAO,SAAQ;AAE3B,4EAA4E;AAC5E,MAAMC,4BAA4B;AAElC,MAAMC,QAAQJ,SAAS;AAQvB;;;;CAIC,GACD,OAAO,eAAeK,sBAAsBC,OAG3C;IACC,OAAQA,QAAQC,OAAO;QACrB,KAAK;YAAiB;gBACpB,OAAOC,+BAA+BF,QAAQG,cAAc,EAAE;YAChE;QACA;YAAS;gBACP,MAAM,IAAIC,MAAM,CAAC,wDAAwD,EAAEJ,QAAQC,OAAO,EAAE;YAC9F;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASI,4BAA4BC,MAG3C;IACC,OAAQA,OAAOL,OAAO;QACpB,KAAK;YAAiB;gBACpB,MAAMM,QAAQD,OAAOE,MAAM,CAACC,GAAG,CAAC,CAACC,QAAU,CAAC,EAAE,EAAEA,MAAMC,KAAK,CAAC,EAAE,EAAED,MAAME,IAAI,CAAC,CAAC,CAAC,EAAEC,IAAI,CAAC;gBACpF,OAAO,CAAC,uBAAuB,EAAEN,OAAO;YAC1C;QACA;YAAS;gBACP,MAAM,IAAIH,MAAM,CAAC,wDAAwD,EAAEE,OAAOL,OAAO,EAAE;YAC7F;IACF;AACF;AAEA;;;;;CAKC,GACD,OAAO,eAAea,yBAAyBd,OAM9C;IACC,MAAM,EAACC,OAAO,EAAEc,cAAc,EAAEC,MAAM,EAAEC,SAAS,EAAEC,OAAO,EAAC,GAAGlB;IAC9D,MAAMmB,UAAUvB,KAAKP,QAAQ4B,YAAY;QAACG,SAAS;YAAChC,SAAS6B;SAAW;IAAA,GAAGI,IAAI,CAAC7B;IAChF,MAAM8B,WAAW,IAAI3B;IACrB2B,SAASC,MAAM,CAAC,WAAWL;IAC3BI,SAASC,MAAM,CAAC,WAAWJ,SAAS;QAClCK,aAAa;QACbC,UAAU;IACZ;IAEA,MAAMC,SAAS,MAAMjC,mBAAmB;QACtCkC,YAAY9B;QACZ+B,aAAa;IACf;IACA,MAAMF,OAAOG,OAAO,CAAC;QACnBC,MAAMR,SAASD,IAAI,CAAC,IAAI/B;QACxByC,SAAST,SAASU,UAAU;QAC5BC,QAAQ;QACRC,KAAK,CAAC,eAAe,EAAEnB,eAAe,QAAQ,CAAC;IACjD;IAEAjB,MAAM,iDAAiDG;IACvDe,OAAOmB,GAAG,CAAC,CAAC,KAAK,EAAE5C,UAAU,QAAQ,YAAY,6BAA6B,CAAC;AACjF;AAEA,kEAAkE,GAClE,eAAeW,+BACbC,cAAsB,EACtBiC,IAAY;IAEZ,MAAMV,SAAS,MAAMjC,mBAAmB;QACtCkC,YAAY9B;QACZ+B,aAAa;IACf;IACA,0EAA0E;IAC1E,MAAM,EAACS,IAAI,EAAC,GAAmC,MAAMX,OAAOG,OAAO,CAAC;QAClES,OAAO;YAACC,OAAO;YAAQpC;QAAc;QACrC+B,KAAK;IACP;IACA,OAAOG,KAAKG,IAAI,CAAC,CAACC,OAASA,KAAKC,WAAW,EAAEN,SAASA,OAAOO;AAC/D"}
|
|
@@ -1,38 +1,27 @@
|
|
|
1
1
|
// The deploy command's view of a workbench app: the resolved interfaces plus
|
|
2
|
-
// the
|
|
3
|
-
// and either gets `null` (plain project — normal
|
|
4
|
-
// to validate the app
|
|
5
|
-
//
|
|
6
|
-
// Node-only (the build-output guard touches the filesystem).
|
|
7
|
-
import { stat } from 'node:fs/promises';
|
|
8
|
-
import { join } from 'node:path';
|
|
2
|
+
// the deploy-time guards that need the app's declarations. `sanity deploy` calls
|
|
3
|
+
// `getWorkbench(config)` once and either gets `null` (plain project — normal
|
|
4
|
+
// deploy) or an object it asks to validate the app before shipping.
|
|
9
5
|
import { resolveWorkbenchApp } from '../../resolveWorkbenchApp.js';
|
|
6
|
+
import { buildViewDeploymentPayload } from './viewDeployment.js';
|
|
10
7
|
export function getWorkbench(cliConfig) {
|
|
11
8
|
const app = resolveWorkbenchApp(cliConfig);
|
|
12
9
|
if (!app) return null;
|
|
13
|
-
const { entry, services, views } = app;
|
|
10
|
+
const { entry, installationConfig, isSingleton, services, views } = app;
|
|
14
11
|
return {
|
|
15
12
|
...app,
|
|
13
|
+
deploySingletonInstallationConfig: isSingleton && !!installationConfig,
|
|
14
|
+
hasInterfaces: !!entry || views.length > 0 || services.length > 0,
|
|
16
15
|
assertDeployable () {
|
|
17
|
-
if (!entry && views.length === 0 && services.length === 0) {
|
|
18
|
-
throw new Error('Nothing to deploy:
|
|
16
|
+
if (!entry && views.length === 0 && services.length === 0 && !installationConfig) {
|
|
17
|
+
throw new Error('Nothing to deploy: the app declares no entry, views, services or installation config. ' + 'Add at least one to the app config.');
|
|
19
18
|
}
|
|
20
19
|
},
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
} catch (err) {
|
|
28
|
-
throw err.code === 'ENOENT' ? new Error(`Directory "${sourceDir}" does not exist`) : err;
|
|
29
|
-
}
|
|
30
|
-
const manifestPath = join(sourceDir, 'mf-manifest.json');
|
|
31
|
-
try {
|
|
32
|
-
await stat(manifestPath);
|
|
33
|
-
} catch (err) {
|
|
34
|
-
throw err.code === 'ENOENT' ? new Error(`"${manifestPath}" does not exist. ` + 'The deploy directory must contain a federation build created with "sanity build".') : err;
|
|
35
|
-
}
|
|
20
|
+
buildViewDeploymentPayload (applicationId) {
|
|
21
|
+
return buildViewDeploymentPayload({
|
|
22
|
+
applicationId,
|
|
23
|
+
views
|
|
24
|
+
});
|
|
36
25
|
}
|
|
37
26
|
};
|
|
38
27
|
}
|
|
@@ -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
|
|
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\ninterface 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 installation 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 deploySingletonInstallationConfig: 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 {entry, installationConfig, isSingleton, services, views} = app\n\n return {\n ...app,\n\n deploySingletonInstallationConfig: isSingleton && !!installationConfig,\n hasInterfaces: !!entry || views.length > 0 || services.length > 0,\n\n assertDeployable() {\n if (!entry && views.length === 0 && services.length === 0 && !installationConfig) {\n throw new Error(\n 'Nothing to deploy: the app declares no entry, views, services or installation 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","entry","installationConfig","isSingleton","services","views","deploySingletonInstallationConfig","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,KAAK,EAAEC,kBAAkB,EAAEC,WAAW,EAAEC,QAAQ,EAAEC,KAAK,EAAC,GAAGL;IAElE,OAAO;QACL,GAAGA,GAAG;QAENM,mCAAmCH,eAAe,CAAC,CAACD;QACpDK,eAAe,CAAC,CAACN,SAASI,MAAMG,MAAM,GAAG,KAAKJ,SAASI,MAAM,GAAG;QAEhEC;YACE,IAAI,CAACR,SAASI,MAAMG,MAAM,KAAK,KAAKJ,SAASI,MAAM,KAAK,KAAK,CAACN,oBAAoB;gBAChF,MAAM,IAAIQ,MACR,2FACE;YAEN;QACF;QAEAb,4BAA2Bc,aAAa;YACtC,OAAOd,2BAA2B;gBAACc;gBAAeN;YAAK;QACzD;IACF;AACF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod/mini';
|
|
2
|
+
/**
|
|
3
|
+
* A view record as persisted to the application service: `type`, `name`, `src`,
|
|
4
|
+
* plus any view-type-specific attributes (passed through for storage).
|
|
5
|
+
*/ const viewRecordSchema = z.looseObject({
|
|
6
|
+
name: z.string().check(z.regex(/^[a-zA-Z0-9_-]+$/, 'View `name` must match /^[a-zA-Z0-9_-]+$/')),
|
|
7
|
+
src: z.string(),
|
|
8
|
+
type: z.enum([
|
|
9
|
+
'panel'
|
|
10
|
+
])
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Payload registering an app's views with the application service on deploy.
|
|
14
|
+
*
|
|
15
|
+
* Phase 1 stub: the service that stores views does not exist yet, so the
|
|
16
|
+
* payload is validated and logged only — never sent. Builds the contract the
|
|
17
|
+
* application-service endpoint will accept.
|
|
18
|
+
*/ const viewDeploymentPayloadSchema = z.object({
|
|
19
|
+
applicationId: z.string(),
|
|
20
|
+
views: z.array(viewRecordSchema)
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Validates an app's declared views into the application-service payload.
|
|
24
|
+
* Throws (via Zod) when a view declaration is malformed.
|
|
25
|
+
*/ export function buildViewDeploymentPayload(input) {
|
|
26
|
+
return viewDeploymentPayloadSchema.parse({
|
|
27
|
+
applicationId: input.applicationId,
|
|
28
|
+
views: input.views ?? []
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=viewDeployment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/viewDeployment.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\n/**\n * A view record as persisted to the application service: `type`, `name`, `src`,\n * plus any view-type-specific attributes (passed through for storage).\n */\nconst viewRecordSchema = z.looseObject({\n name: z.string().check(z.regex(/^[a-zA-Z0-9_-]+$/, 'View `name` must match /^[a-zA-Z0-9_-]+$/')),\n src: z.string(),\n type: z.enum(['panel']),\n})\n\n/**\n * Payload registering an app's views with the application service on deploy.\n *\n * Phase 1 stub: the service that stores views does not exist yet, so the\n * payload is validated and logged only — never sent. Builds the contract the\n * application-service endpoint will accept.\n */\nconst viewDeploymentPayloadSchema = z.object({\n applicationId: z.string(),\n views: z.array(viewRecordSchema),\n})\n\nexport type ViewDeploymentPayload = z.infer<typeof viewDeploymentPayloadSchema>\n\n/**\n * Validates an app's declared views into the application-service payload.\n * Throws (via Zod) when a view declaration is malformed.\n */\nexport function buildViewDeploymentPayload(input: {\n applicationId: string\n views?: ReadonlyArray<Record<string, unknown>>\n}): ViewDeploymentPayload {\n return viewDeploymentPayloadSchema.parse({\n applicationId: input.applicationId,\n views: input.views ?? [],\n })\n}\n"],"names":["z","viewRecordSchema","looseObject","name","string","check","regex","src","type","enum","viewDeploymentPayloadSchema","object","applicationId","views","array","buildViewDeploymentPayload","input","parse"],"mappings":"AAAA,SAAQA,CAAC,QAAO,WAAU;AAE1B;;;CAGC,GACD,MAAMC,mBAAmBD,EAAEE,WAAW,CAAC;IACrCC,MAAMH,EAAEI,MAAM,GAAGC,KAAK,CAACL,EAAEM,KAAK,CAAC,oBAAoB;IACnDC,KAAKP,EAAEI,MAAM;IACbI,MAAMR,EAAES,IAAI,CAAC;QAAC;KAAQ;AACxB;AAEA;;;;;;CAMC,GACD,MAAMC,8BAA8BV,EAAEW,MAAM,CAAC;IAC3CC,eAAeZ,EAAEI,MAAM;IACvBS,OAAOb,EAAEc,KAAK,CAACb;AACjB;AAIA;;;CAGC,GACD,OAAO,SAASc,2BAA2BC,KAG1C;IACC,OAAON,4BAA4BO,KAAK,CAAC;QACvCL,eAAeI,MAAMJ,aAAa;QAClCC,OAAOG,MAAMH,KAAK,IAAI,EAAE;IAC1B;AACF"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { isWorkbenchApp } from '
|
|
1
|
+
import { isWorkbenchApp, readInstallationConfig } from '../../defineApp.js';
|
|
2
2
|
/**
|
|
3
|
-
* Map
|
|
4
|
-
*
|
|
3
|
+
* Map a workbench app's declarations to the interface records forwarded on its
|
|
4
|
+
* registry entry: `views` → panels, `services` → workers, `entry` → the
|
|
5
5
|
* navigable `app` view (`entry_point` is the raw `src`, not a resolved URL).
|
|
6
6
|
* `undefined` for a non-branded app; a studio that declares `entry` is rejected
|
|
7
|
-
* (studio app views are not implemented yet).
|
|
7
|
+
* (studio app views are not implemented yet). The installation config is not an
|
|
8
|
+
* interface — see {@link deriveInstallationConfigs}.
|
|
8
9
|
*/ export function deriveInterfaces(app, options) {
|
|
9
10
|
if (!isWorkbenchApp(app)) return undefined;
|
|
10
11
|
if (!options.isApp && app.entry !== undefined) {
|
|
@@ -30,5 +31,47 @@ import { isWorkbenchApp } from '@sanity/cli-core';
|
|
|
30
31
|
]
|
|
31
32
|
];
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* The named source files a config's generated module is built from, dispatched
|
|
36
|
+
* per app type — the projection the exposes-set id keys on, so the generic HMR
|
|
37
|
+
* tracker owns none of the per-type shape. Throws on an app type it can't
|
|
38
|
+
* handle, so a new config family has to register its shape here.
|
|
39
|
+
*/ export function deriveInstallationConfigEntries(config) {
|
|
40
|
+
switch(config.appType){
|
|
41
|
+
case 'media-library':
|
|
42
|
+
{
|
|
43
|
+
return config.fields.map((field)=>({
|
|
44
|
+
name: field.name,
|
|
45
|
+
src: field.src
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
default:
|
|
49
|
+
{
|
|
50
|
+
throw new Error(`Cannot derive entries for unknown installation config appType: ${config.appType}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The fields' schema *values* can't serialize — the workbench loads them from
|
|
56
|
+
* the federation module. `src` stays on so the exposes-set id keys on it and a
|
|
57
|
+
* repoint rebuilds. `appType` routes the config to the singleton (no app id to
|
|
58
|
+
* key on).
|
|
59
|
+
*/ export function deriveInstallationConfigs(app) {
|
|
60
|
+
if (!isWorkbenchApp(app)) return [];
|
|
61
|
+
const installationConfig = readInstallationConfig(app);
|
|
62
|
+
if (!installationConfig) return [];
|
|
63
|
+
return [
|
|
64
|
+
{
|
|
65
|
+
appType: installationConfig.appType,
|
|
66
|
+
fields: installationConfig.fields.map((field)=>({
|
|
67
|
+
name: field.name,
|
|
68
|
+
public: field.public,
|
|
69
|
+
src: field.src,
|
|
70
|
+
title: field.title
|
|
71
|
+
})),
|
|
72
|
+
moduleName: app.name
|
|
73
|
+
}
|
|
74
|
+
];
|
|
75
|
+
}
|
|
33
76
|
|
|
34
77
|
//# sourceMappingURL=deriveInterfaces.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/dev/deriveInterfaces.ts"],"sourcesContent":["import {type CliConfig
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/deriveInterfaces.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\n\nimport {isWorkbenchApp, readInstallationConfig} 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 installation config on the dev-server registry entry. */\nexport type DevServerConfig = NonNullable<DevServerManifest['installationConfigs']>[number]\n\n/**\n * Map a workbench app's declarations to the interface records forwarded on its\n * registry entry: `views` → panels, `services` → workers, `entry` → the\n * navigable `app` view (`entry_point` is the raw `src`, not a resolved URL).\n * `undefined` for a non-branded app; a studio that declares `entry` is rejected\n * (studio app views are not implemented yet). The installation config is not an\n * interface — see {@link deriveInstallationConfigs}.\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 return [\n ...(app.views?.map((view) => ({\n entry_point: view.src,\n interface_type: view.type,\n name: view.name,\n })) ?? []),\n ...(app.services?.map((service) => ({\n entry_point: service.src,\n interface_type: service.type,\n name: service.name,\n })) ?? []),\n ...(app.entry === undefined\n ? []\n : [{entry_point: app.entry, interface_type: 'app' as const, name: app.name}]),\n ]\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 deriveInstallationConfigEntries(\n config: DevServerConfig,\n): {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(\n `Cannot derive entries for unknown installation config appType: ${config.appType}`,\n )\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).\n */\nexport function deriveInstallationConfigs(app: CliConfig['app']): DevServerConfig[] {\n if (!isWorkbenchApp(app)) return []\n const installationConfig = readInstallationConfig(app)\n if (!installationConfig) return []\n return [\n {\n appType: installationConfig.appType,\n fields: installationConfig.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 },\n ]\n}\n"],"names":["isWorkbenchApp","readInstallationConfig","deriveInterfaces","app","options","undefined","isApp","entry","Error","views","map","view","entry_point","src","interface_type","type","name","services","service","deriveInstallationConfigEntries","config","appType","fields","field","deriveInstallationConfigs","installationConfig","public","title","moduleName"],"mappings":"AAEA,SAAQA,cAAc,EAAEC,sBAAsB,QAAO,qBAAoB;AASzE;;;;;;;CAOC,GACD,OAAO,SAASC,iBACdC,GAAqB,EACrBC,OAAyB;IAEzB,IAAI,CAACJ,eAAeG,MAAM,OAAOE;IAEjC,IAAI,CAACD,QAAQE,KAAK,IAAIH,IAAII,KAAK,KAAKF,WAAW;QAC7C,MAAM,IAAIG,MAAM;IAClB;IAEA,OAAO;WACDL,IAAIM,KAAK,EAAEC,IAAI,CAACC,OAAU,CAAA;gBAC5BC,aAAaD,KAAKE,GAAG;gBACrBC,gBAAgBH,KAAKI,IAAI;gBACzBC,MAAML,KAAKK,IAAI;YACjB,CAAA,MAAO,EAAE;WACLb,IAAIc,QAAQ,EAAEP,IAAI,CAACQ,UAAa,CAAA;gBAClCN,aAAaM,QAAQL,GAAG;gBACxBC,gBAAgBI,QAAQH,IAAI;gBAC5BC,MAAME,QAAQF,IAAI;YACpB,CAAA,MAAO,EAAE;WACLb,IAAII,KAAK,KAAKF,YACd,EAAE,GACF;YAAC;gBAACO,aAAaT,IAAII,KAAK;gBAAEO,gBAAgB;gBAAgBE,MAAMb,IAAIa,IAAI;YAAA;SAAE;KAC/E;AACH;AAEA;;;;;CAKC,GACD,OAAO,SAASG,gCACdC,MAAuB;IAEvB,OAAQA,OAAOC,OAAO;QACpB,KAAK;YAAiB;gBACpB,OAAOD,OAAOE,MAAM,CAACZ,GAAG,CAAC,CAACa,QAAW,CAAA;wBAACP,MAAMO,MAAMP,IAAI;wBAAEH,KAAKU,MAAMV,GAAG;oBAAA,CAAA;YACxE;QACA;YAAS;gBACP,MAAM,IAAIL,MACR,CAAC,+DAA+D,EAAEY,OAAOC,OAAO,EAAE;YAEtF;IACF;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASG,0BAA0BrB,GAAqB;IAC7D,IAAI,CAACH,eAAeG,MAAM,OAAO,EAAE;IACnC,MAAMsB,qBAAqBxB,uBAAuBE;IAClD,IAAI,CAACsB,oBAAoB,OAAO,EAAE;IAClC,OAAO;QACL;YACEJ,SAASI,mBAAmBJ,OAAO;YACnCC,QAAQG,mBAAmBH,MAAM,CAACZ,GAAG,CAAC,CAACa,QAAW,CAAA;oBAChDP,MAAMO,MAAMP,IAAI;oBAChBU,QAAQH,MAAMG,MAAM;oBACpBb,KAAKU,MAAMV,GAAG;oBACdc,OAAOJ,MAAMI,KAAK;gBACpB,CAAA;YACAC,YAAYzB,IAAIa,IAAI;QACtB;KACD;AACH"}
|