@sanity/cli 7.7.1 → 7.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +17 -2
  2. package/dist/actions/auth/ensureAuthenticated.js +1 -1
  3. package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
  4. package/dist/actions/auth/login/validateToken.js +1 -1
  5. package/dist/actions/auth/login/validateToken.js.map +1 -1
  6. package/dist/actions/build/buildApp.js +2 -3
  7. package/dist/actions/build/buildApp.js.map +1 -1
  8. package/dist/actions/build/buildStudio.js +1 -2
  9. package/dist/actions/build/buildStudio.js.map +1 -1
  10. package/dist/actions/deploy/createUserApplication.js +8 -8
  11. package/dist/actions/deploy/createUserApplication.js.map +1 -1
  12. package/dist/actions/deploy/deployApp.js +128 -26
  13. package/dist/actions/deploy/deployApp.js.map +1 -1
  14. package/dist/actions/deploy/deployChecks.js +97 -72
  15. package/dist/actions/deploy/deployChecks.js.map +1 -1
  16. package/dist/actions/deploy/deployRunner.js +8 -8
  17. package/dist/actions/deploy/deployRunner.js.map +1 -1
  18. package/dist/actions/deploy/deployStudio.js +129 -23
  19. package/dist/actions/deploy/deployStudio.js.map +1 -1
  20. package/dist/actions/deploy/deploymentPlan.js +29 -30
  21. package/dist/actions/deploy/deploymentPlan.js.map +1 -1
  22. package/dist/actions/deploy/findUserApplication.js +39 -14
  23. package/dist/actions/deploy/findUserApplication.js.map +1 -1
  24. package/dist/actions/deploy/resolveDeployTarget.js +44 -0
  25. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  26. package/dist/actions/deploy/urlUtils.js +0 -4
  27. package/dist/actions/deploy/urlUtils.js.map +1 -1
  28. package/dist/actions/dev/servers/getDevServerConfig.js +1 -1
  29. package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
  30. package/dist/actions/dev/servers/startStudioDevServer.js +1 -2
  31. package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
  32. package/dist/actions/documents/types.js.map +1 -1
  33. package/dist/actions/documents/validate.js +2 -1
  34. package/dist/actions/documents/validate.js.map +1 -1
  35. package/dist/actions/graphql/SchemaError.js.map +1 -1
  36. package/dist/actions/graphql/extractGraphQLAPIs.js +3 -1
  37. package/dist/actions/graphql/extractGraphQLAPIs.js.map +1 -1
  38. package/dist/actions/manifest/extractCoreAppManifest.js +1 -1
  39. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  40. package/dist/actions/organizations/getOrganization.js +1 -1
  41. package/dist/actions/organizations/getOrganization.js.map +1 -1
  42. package/dist/actions/schema/validateAction.js +1 -1
  43. package/dist/actions/schema/validateAction.js.map +1 -1
  44. package/dist/actions/skills/setupSkills.js +1 -1
  45. package/dist/actions/skills/setupSkills.js.map +1 -1
  46. package/dist/actions/undeploy/adapters.js +101 -0
  47. package/dist/actions/undeploy/adapters.js.map +1 -0
  48. package/dist/actions/undeploy/runUndeploy.js +179 -0
  49. package/dist/actions/undeploy/runUndeploy.js.map +1 -0
  50. package/dist/actions/undeploy/undeployPlan.js +123 -0
  51. package/dist/actions/undeploy/undeployPlan.js.map +1 -0
  52. package/dist/commands/datasets/copy.js +2 -1
  53. package/dist/commands/datasets/copy.js.map +1 -1
  54. package/dist/commands/documents/validate.js +18 -17
  55. package/dist/commands/documents/validate.js.map +1 -1
  56. package/dist/commands/mcp/configure.js +1 -1
  57. package/dist/commands/mcp/configure.js.map +1 -1
  58. package/dist/commands/projects/create.js +18 -13
  59. package/dist/commands/projects/create.js.map +1 -1
  60. package/dist/commands/projects/list.js +2 -1
  61. package/dist/commands/projects/list.js.map +1 -1
  62. package/dist/commands/schemas/validate.js +3 -2
  63. package/dist/commands/schemas/validate.js.map +1 -1
  64. package/dist/commands/skills/install.js +1 -1
  65. package/dist/commands/skills/install.js.map +1 -1
  66. package/dist/commands/tokens/list.js +1 -1
  67. package/dist/commands/tokens/list.js.map +1 -1
  68. package/dist/commands/undeploy.js +47 -73
  69. package/dist/commands/undeploy.js.map +1 -1
  70. package/dist/server/devServer.js.map +1 -1
  71. package/dist/util/checks.js +83 -0
  72. package/dist/util/checks.js.map +1 -0
  73. package/dist/util/toStderrOutput.js +13 -0
  74. package/dist/util/toStderrOutput.js.map +1 -0
  75. package/oclif.manifest.json +28 -1
  76. package/package.json +7 -8
  77. package/dist/actions/undeploy/getStudioOrAppUserApplication.js +0 -36
  78. package/dist/actions/undeploy/getStudioOrAppUserApplication.js.map +0 -1
  79. package/dist/util/compareDependencyVersions.js +0 -110
  80. package/dist/util/compareDependencyVersions.js.map +0 -1
  81. package/dist/util/getErrorMessage.js +0 -28
  82. package/dist/util/getErrorMessage.js.map +0 -1
@@ -4,16 +4,16 @@ import { createGzip } from 'node:zlib';
4
4
  import { formatSchemaValidation, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
5
5
  import { exitCodes } from '@sanity/cli-core';
6
6
  import { spinner } from '@sanity/cli-core/ux';
7
- import { getWorkbench } from '@sanity/workbench-cli/deploy';
7
+ import { buildExposes, deployStudio as deployWorkbenchStudio, getApplicationUrl, getWorkbench } from '@sanity/workbench-cli/deploy';
8
8
  import { pack } from 'tar-fs';
9
9
  import { createDeployment } from '../../services/userApplications.js';
10
10
  import { getAppId } from '../../util/appId.js';
11
- import { NO_PROJECT_ID } from '../../util/errorMessages.js';
11
+ import { NO_ORGANIZATION_ID, NO_PROJECT_ID } from '../../util/errorMessages.js';
12
12
  import { buildStudio } from '../build/buildStudio.js';
13
13
  import { createStudioUserApplication } from './createUserApplication.js';
14
14
  import { checkAutoUpdates, checkBuild, checkPackageVersion, checkStudioTarget, verifyOutputDir } from './deployChecks.js';
15
15
  import { deployDebug } from './deployDebug.js';
16
- import { listDeploymentFiles } from './deploymentPlan.js';
16
+ import { listDeploymentFiles, reportExposes } from './deploymentPlan.js';
17
17
  import { runDeploy } from './deployRunner.js';
18
18
  import { deployStudioSchemasAndManifests } from './deployStudioSchemasAndManifests.js';
19
19
  import { findUserApplicationForStudio } from './findUserApplication.js';
@@ -29,16 +29,15 @@ export function deployStudio(options) {
29
29
  const { cliConfig, flags, output, sourceDir } = options;
30
30
  const workDir = options.projectRoot.directory;
31
31
  const isExternal = !!flags.external;
32
- const isWorkbenchApp = getWorkbench(cliConfig) !== null;
32
+ const workbench = getWorkbench(cliConfig);
33
+ const isWorkbenchApp = workbench !== null;
33
34
  const projectId = cliConfig.api?.projectId;
35
+ const organizationId = cliConfig.app?.organizationId;
36
+ const appId = getAppId(cliConfig);
34
37
  const dryRun = !!flags['dry-run'];
35
- const isAutoUpdating = checkAutoUpdates(reporter, {
36
- cliConfig,
37
- flags
38
- });
38
+ // A federated app deploys through Sanity's build/hosting pipeline, which
39
+ // --external skips — fail before doing any other work.
39
40
  if (isExternal && isWorkbenchApp) {
40
- // A federated app deploys through Sanity's build/hosting pipeline, which
41
- // --external skips.
42
41
  reporter.report({
43
42
  exitCode: exitCodes.USAGE_ERROR,
44
43
  message: 'Deploying a federated application to an external host is not yet supported',
@@ -46,6 +45,11 @@ export function deployStudio(options) {
46
45
  status: 'fail'
47
46
  });
48
47
  }
48
+ const appTitle = workbench ? flags.title?.trim() || cliConfig.app?.title?.trim() || workbench.name : '';
49
+ const isAutoUpdating = checkAutoUpdates(reporter, {
50
+ cliConfig,
51
+ flags
52
+ });
49
53
  const version = await checkPackageVersion(reporter, {
50
54
  moduleName: STUDIO_PACKAGE,
51
55
  workDir
@@ -58,10 +62,32 @@ export function deployStudio(options) {
58
62
  solution: 'Add `api.projectId` to sanity.cli.ts',
59
63
  status: 'fail'
60
64
  });
61
- const application = await resolveStudioApplication(options, {
62
- dryRun,
63
- reporter
64
- });
65
+ // Workbench studios deploy to Brett (which needs the org); plain studios
66
+ // resolve/create on user-applications, unchanged.
67
+ let application = null;
68
+ let studioCreated = false;
69
+ if (workbench && !isExternal) {
70
+ reporter.report(organizationId ? {
71
+ message: `Organization: ${organizationId}`,
72
+ status: 'pass'
73
+ } : {
74
+ message: NO_ORGANIZATION_ID,
75
+ solution: 'Add `app.organizationId` to sanity.cli.ts',
76
+ status: 'fail'
77
+ });
78
+ await checkStudioTarget(reporter, {
79
+ appId,
80
+ isWorkbenchApp: true,
81
+ studioHost: cliConfig.studioHost,
82
+ title: appTitle
83
+ });
84
+ } else {
85
+ ;
86
+ ({ application, created: studioCreated } = await resolveStudioApplication(options, {
87
+ dryRun,
88
+ reporter
89
+ }));
90
+ }
65
91
  await checkBuild(reporter, {
66
92
  build: ()=>buildStudio({
67
93
  autoUpdatesEnabled: isAutoUpdating,
@@ -85,14 +111,63 @@ export function deployStudio(options) {
85
111
  sourceDir
86
112
  });
87
113
  }
114
+ // Report the exposes deploying with the studio, both modes. External studios
115
+ // host their own bundle, so nothing registers.
116
+ const exposes = workbench && !isExternal ? reportExposes(reporter, workbench) : [];
88
117
  // Dry run stops here — everything below mutates.
89
118
  if (dryRun) return;
90
119
  // A real deploy has already exited if anything failed; landing here without a
91
- // resolved application or version means the deploy target was never resolved.
92
- if (!application || !version) return;
120
+ // resolved version means the deploy target was never resolved.
121
+ if (!version) return;
93
122
  const studioManifest = await uploadStudioSchema(options, {
94
123
  isExternal
95
124
  });
125
+ // Workbench studios deploy to Brett; plain studios use user-applications.
126
+ if (workbench && !isExternal && organizationId) {
127
+ const { applicationId } = await deployWorkbenchStudio({
128
+ appId,
129
+ interfaces: buildExposes(workbench, {
130
+ appName: workbench.name,
131
+ appTitle,
132
+ exposesAppView: true,
133
+ version
134
+ }),
135
+ isAutoUpdating,
136
+ organizationId,
137
+ output,
138
+ projectId,
139
+ sourceDir,
140
+ studioHost: cliConfig.studioHost,
141
+ title: appTitle,
142
+ version,
143
+ workspaces: toWorkspaces(studioManifest)
144
+ });
145
+ const url = getApplicationUrl({
146
+ id: applicationId,
147
+ organizationId,
148
+ type: 'studio'
149
+ });
150
+ logWorkbenchStudioDeployed({
151
+ applicationId,
152
+ cliConfig,
153
+ output,
154
+ url
155
+ });
156
+ return {
157
+ applicationType: 'studio',
158
+ applicationVersion: version,
159
+ ...exposes.length > 0 ? {
160
+ exposes
161
+ } : {},
162
+ target: {
163
+ action: appId ? 'update' : 'create',
164
+ applicationId,
165
+ title: appTitle,
166
+ url
167
+ }
168
+ };
169
+ }
170
+ if (!application) return;
96
171
  const location = await shipStudioDeployment({
97
172
  application,
98
173
  isAutoUpdating,
@@ -105,6 +180,7 @@ export function deployStudio(options) {
105
180
  applicationType: 'studio',
106
181
  applicationVersion: version,
107
182
  target: {
183
+ action: studioCreated ? 'create' : 'update',
108
184
  applicationId: application.id,
109
185
  title: application.title ?? null,
110
186
  url: location
@@ -117,22 +193,27 @@ export function deployStudio(options) {
117
193
  */ async function resolveStudioApplication(options, { dryRun, reporter }) {
118
194
  const { cliConfig, flags, output } = options;
119
195
  const isExternal = !!flags.external;
196
+ const appId = getAppId(cliConfig);
120
197
  // Sets the title on a newly registered studio; blank falls back to undefined
121
198
  const title = flags.title?.trim() || undefined;
122
199
  if (dryRun) {
123
200
  await checkStudioTarget(reporter, {
124
- appId: getAppId(cliConfig),
201
+ appId,
125
202
  isExternal,
126
203
  projectId: cliConfig.api?.projectId,
127
204
  studioHost: cliConfig.studioHost,
128
205
  title,
129
206
  urlFlag: flags.url
130
207
  });
131
- return null;
208
+ return {
209
+ application: null,
210
+ created: false
211
+ };
132
212
  }
133
213
  const projectId = cliConfig.api?.projectId ?? '';
134
- let application = await findUserApplicationForStudio({
135
- appId: getAppId(cliConfig),
214
+ // `created` is true when a configured-but-unregistered host was just registered.
215
+ const { application, created } = await findUserApplicationForStudio({
216
+ appId,
136
217
  isExternal,
137
218
  output,
138
219
  projectId,
@@ -150,15 +231,22 @@ export function deployStudio(options) {
150
231
  output.log('To deploy your Sanity Studio to our hosted sanity.studio service,');
151
232
  output.log('you will need one. Please enter the subdomain you want to use.');
152
233
  }
153
- application = await createStudioUserApplication({
234
+ const registered = await createStudioUserApplication({
154
235
  projectId,
155
236
  title,
156
237
  urlType: isExternal ? 'external' : 'internal'
157
238
  });
158
- deployDebug('Created user application', application);
239
+ deployDebug('Created user application', registered);
240
+ return {
241
+ application: registered,
242
+ created: true
243
+ };
159
244
  }
160
245
  deployDebug('Found user application', application);
161
- return application;
246
+ return {
247
+ application,
248
+ created
249
+ };
162
250
  }
163
251
  /** Extracts the studio schema and manifest and uploads them to the schema store. */ async function uploadStudioSchema(options, { isExternal }) {
164
252
  const { cliConfig, flags, output, projectRoot, sourceDir } = options;
@@ -236,6 +324,24 @@ export default defineCliConfig({
236
324
  output.log(`\n${example}`);
237
325
  return location;
238
326
  }
327
+ function toWorkspaces(manifest) {
328
+ return (manifest?.workspaces ?? []).map((workspace)=>({
329
+ basePath: workspace.basePath,
330
+ dataset: workspace.dataset,
331
+ icon: workspace.icon,
332
+ name: workspace.name,
333
+ projectId: workspace.projectId,
334
+ subtitle: workspace.subtitle,
335
+ title: workspace.title
336
+ }));
337
+ }
338
+ /** Renders the workbench studio's deploy result; the appId hint shows only when none is configured. */ function logWorkbenchStudioDeployed({ applicationId, cliConfig, output, url }) {
339
+ output.log(`\nSuccess! Studio deployed to ${styleText('cyan', url)}`);
340
+ if (getAppId(cliConfig)) return;
341
+ output.log(`\nAdd ${styleText('cyan', `appId: '${applicationId}'`)}`);
342
+ output.log(`to the \`deployment\` section in sanity.cli.js or sanity.cli.ts`);
343
+ output.log(`to avoid prompting for application id on next deploy.`);
344
+ }
239
345
  function studioBuildSkipReason({ build, isExternal }) {
240
346
  if (isExternal) return 'Build skipped for externally hosted studios';
241
347
  if (!build) return 'Build skipped (--no-build) — validating existing output directory';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/deployStudio.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip, type Gzip} from 'node:zlib'\n\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {getWorkbench} from '@sanity/workbench-cli/deploy'\nimport {type StudioManifest} from 'sanity'\nimport {pack} from 'tar-fs'\n\nimport {createDeployment, type UserApplication} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {buildStudio} from '../build/buildStudio.js'\nimport {createStudioUserApplication} from './createUserApplication.js'\nimport {\n checkAutoUpdates,\n checkBuild,\n checkPackageVersion,\n type CheckReporter,\n checkStudioTarget,\n verifyOutputDir,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {listDeploymentFiles} from './deploymentPlan.js'\nimport {type DeployResult, runDeploy} from './deployRunner.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\n\nconst STUDIO_PACKAGE = 'sanity'\n\nexport function deployStudio(options: DeployAppOptions): Promise<void> {\n return runDeploy(options, {\n listFiles: ({flags, projectRoot, sourceDir}) =>\n flags.external ? Promise.resolve([]) : listDeploymentFiles(sourceDir, projectRoot.directory),\n run: runStudioDeployment,\n type: 'studio',\n })\n}\n\n/** Validates the deploy, extracts and uploads the schema, and ships the build. */\nasync function runStudioDeployment(\n options: DeployAppOptions,\n reporter: CheckReporter,\n): Promise<DeployResult | void> {\n const {cliConfig, flags, output, sourceDir} = options\n const workDir = options.projectRoot.directory\n const isExternal = !!flags.external\n const isWorkbenchApp = getWorkbench(cliConfig) !== null\n const projectId = cliConfig.api?.projectId\n const dryRun = !!flags['dry-run']\n\n const isAutoUpdating = checkAutoUpdates(reporter, {cliConfig, flags})\n\n if (isExternal && isWorkbenchApp) {\n // A federated app deploys through Sanity's build/hosting pipeline, which\n // --external skips.\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'Deploying a federated application to an external host is not yet supported',\n solution: 'Remove the --external flag to deploy to Sanity hosting',\n status: 'fail',\n })\n }\n\n const version = await checkPackageVersion(reporter, {\n moduleName: STUDIO_PACKAGE,\n workDir,\n })\n\n reporter.report(\n projectId\n ? {message: `Project: ${projectId}`, status: 'pass'}\n : {\n message: NO_PROJECT_ID,\n solution: 'Add `api.projectId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n\n const application = await resolveStudioApplication(options, {dryRun, reporter})\n\n await checkBuild(reporter, {\n build: () =>\n buildStudio({\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n }),\n skipReason: studioBuildSkipReason({build: flags.build, isExternal}),\n successMessage: 'Studio built',\n })\n\n if (!isExternal) {\n await verifyOutputDir({isWorkbenchApp, reporter, sourceDir})\n }\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return\n\n // A real deploy has already exited if anything failed; landing here without a\n // resolved application or version means the deploy target was never resolved.\n if (!application || !version) return\n\n const studioManifest = await uploadStudioSchema(options, {isExternal})\n const location = await shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n })\n\n return {\n applicationType: 'studio',\n applicationVersion: version,\n target: {applicationId: application.id, title: application.title ?? null, url: location},\n }\n}\n\n/**\n * Finds the application a real deploy targets, registering a studio host when\n * none is configured. A dry run resolves and reports the target read-only instead.\n */\nasync function resolveStudioApplication(\n options: DeployAppOptions,\n {dryRun, reporter}: {dryRun: boolean; reporter: CheckReporter},\n): Promise<UserApplication | null> {\n const {cliConfig, flags, output} = options\n const isExternal = !!flags.external\n // Sets the title on a newly registered studio; blank falls back to undefined\n const title = flags.title?.trim() || undefined\n\n if (dryRun) {\n await checkStudioTarget(reporter, {\n appId: getAppId(cliConfig),\n isExternal,\n projectId: cliConfig.api?.projectId,\n studioHost: cliConfig.studioHost,\n title,\n urlFlag: flags.url,\n })\n return null\n }\n\n const projectId = cliConfig.api?.projectId ?? ''\n let application = await findUserApplicationForStudio({\n appId: getAppId(cliConfig),\n isExternal,\n output,\n projectId,\n studioHost: cliConfig.studioHost,\n title,\n unattended: !!flags.yes,\n urlFlag: flags.url,\n })\n\n if (!application) {\n if (isExternal) {\n output.log('Your project has not been registered with an external studio URL.')\n output.log('Please enter the full URL where your studio is hosted.')\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log('To deploy your Sanity Studio to our hosted sanity.studio service,')\n output.log('you will need one. Please enter the subdomain you want to use.')\n }\n\n application = await createStudioUserApplication({\n projectId,\n title,\n urlType: isExternal ? 'external' : 'internal',\n })\n deployDebug('Created user application', application)\n }\n\n deployDebug('Found user application', application)\n return application\n}\n\n/** Extracts the studio schema and manifest and uploads them to the schema store. */\nasync function uploadStudioSchema(\n options: DeployAppOptions,\n {isExternal}: {isExternal: boolean},\n): Promise<StudioManifest | null> {\n const {cliConfig, flags, output, projectRoot, sourceDir} = options\n\n let studioManifest: StudioManifest | null = null\n try {\n studioManifest = await deployStudioSchemasAndManifests(\n {\n configPath: projectRoot.path,\n isExternal,\n outPath: `${sourceDir}/static`,\n projectId: cliConfig.api?.projectId ?? '',\n schemaRequired: flags['schema-required'],\n verbose: flags.verbose,\n workDir: projectRoot.directory,\n },\n output,\n )\n } catch (error) {\n deployDebug('Error deploying studio schemas and manifests', error)\n if (error instanceof SchemaExtractionError) {\n output.error(formatSchemaValidation(error.validation || []), {exit: 1})\n }\n output.error(`Error deploying studio schemas and manifests: ${error}`, {exit: 1})\n }\n\n if (!studioManifest) {\n output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {\n exit: 1,\n })\n }\n\n return studioManifest\n}\n\nasync function shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n}: {\n application: UserApplication\n isAutoUpdating: boolean\n isExternal: boolean\n options: DeployAppOptions\n studioManifest: StudioManifest | null\n version: string\n}): Promise<string> {\n const {cliConfig, output, sourceDir} = options\n\n let tarball: Gzip | undefined\n if (!isExternal) {\n tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n }\n\n const spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start()\n let location: string\n try {\n ;({location} = await createDeployment({\n applicationId: application.id,\n isApp: false,\n isAutoUpdating,\n manifest: studioManifest,\n projectId: cliConfig.api?.projectId,\n tarball,\n version,\n }))\n } catch (error) {\n spin.fail()\n throw error\n }\n spin.succeed()\n\n const named = application.title ? ` — \"${application.title}\"` : ''\n output.log(\n isExternal\n ? `\\nSuccess! Studio registered${named}`\n : `\\nSuccess! Studio deployed to ${styleText('cyan', location)}${named}`,\n )\n\n if (getAppId(cliConfig)) return location\n\n const example = `Example:\nexport default defineCliConfig({\n //…\n deployment: {\n ${styleText('cyan', `appId: '${application.id}'`)},\n },\n //…\n})`\n output.log(`\\nAdd ${styleText('cyan', `appId: '${application.id}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n output.log(`\\n${example}`)\n\n return location\n}\n\nfunction studioBuildSkipReason({build, isExternal}: {build: boolean; isExternal: boolean}) {\n if (isExternal) return 'Build skipped for externally hosted studios'\n if (!build) return 'Build skipped (--no-build) — validating existing output directory'\n return\n}\n"],"names":["basename","dirname","styleText","createGzip","formatSchemaValidation","SchemaExtractionError","exitCodes","spinner","getWorkbench","pack","createDeployment","getAppId","NO_PROJECT_ID","buildStudio","createStudioUserApplication","checkAutoUpdates","checkBuild","checkPackageVersion","checkStudioTarget","verifyOutputDir","deployDebug","listDeploymentFiles","runDeploy","deployStudioSchemasAndManifests","findUserApplicationForStudio","STUDIO_PACKAGE","deployStudio","options","listFiles","flags","projectRoot","sourceDir","external","Promise","resolve","directory","run","runStudioDeployment","type","reporter","cliConfig","output","workDir","isExternal","isWorkbenchApp","projectId","api","dryRun","isAutoUpdating","report","exitCode","USAGE_ERROR","message","solution","status","version","moduleName","application","resolveStudioApplication","build","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","studioBuildSkipReason","successMessage","studioManifest","uploadStudioSchema","location","shipStudioDeployment","applicationType","applicationVersion","target","applicationId","id","title","url","trim","undefined","appId","studioHost","urlFlag","unattended","yes","log","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","exit","tarball","entries","pipe","spin","start","isApp","manifest","fail","succeed","named","example"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAC3C,SAAQC,SAAS,QAAO,YAAW;AACnC,SAAQC,UAAU,QAAkB,YAAW;AAE/C,SAAQC,sBAAsB,EAAEC,qBAAqB,QAAO,sCAAqC;AACjG,SAAQC,SAAS,QAAO,mBAAkB;AAC1C,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,YAAY,QAAO,+BAA8B;AAEzD,SAAQC,IAAI,QAAO,SAAQ;AAE3B,SAAQC,gBAAgB,QAA6B,qCAAoC;AACzF,SAAQC,QAAQ,QAAO,sBAAqB;AAC5C,SAAQC,aAAa,QAAO,8BAA6B;AACzD,SAAQC,WAAW,QAAO,0BAAyB;AACnD,SAAQC,2BAA2B,QAAO,6BAA4B;AACtE,SACEC,gBAAgB,EAChBC,UAAU,EACVC,mBAAmB,EAEnBC,iBAAiB,EACjBC,eAAe,QACV,oBAAmB;AAC1B,SAAQC,WAAW,QAAO,mBAAkB;AAC5C,SAAQC,mBAAmB,QAAO,sBAAqB;AACvD,SAA2BC,SAAS,QAAO,oBAAmB;AAC9D,SAAQC,+BAA+B,QAAO,uCAAsC;AACpF,SAAQC,4BAA4B,QAAO,2BAA0B;AAGrE,MAAMC,iBAAiB;AAEvB,OAAO,SAASC,aAAaC,OAAyB;IACpD,OAAOL,UAAUK,SAAS;QACxBC,WAAW,CAAC,EAACC,KAAK,EAAEC,WAAW,EAAEC,SAAS,EAAC,GACzCF,MAAMG,QAAQ,GAAGC,QAAQC,OAAO,CAAC,EAAE,IAAIb,oBAAoBU,WAAWD,YAAYK,SAAS;QAC7FC,KAAKC;QACLC,MAAM;IACR;AACF;AAEA,gFAAgF,GAChF,eAAeD,oBACbV,OAAyB,EACzBY,QAAuB;IAEvB,MAAM,EAACC,SAAS,EAAEX,KAAK,EAAEY,MAAM,EAAEV,SAAS,EAAC,GAAGJ;IAC9C,MAAMe,UAAUf,QAAQG,WAAW,CAACK,SAAS;IAC7C,MAAMQ,aAAa,CAAC,CAACd,MAAMG,QAAQ;IACnC,MAAMY,iBAAiBpC,aAAagC,eAAe;IACnD,MAAMK,YAAYL,UAAUM,GAAG,EAAED;IACjC,MAAME,SAAS,CAAC,CAAClB,KAAK,CAAC,UAAU;IAEjC,MAAMmB,iBAAiBjC,iBAAiBwB,UAAU;QAACC;QAAWX;IAAK;IAEnE,IAAIc,cAAcC,gBAAgB;QAChC,yEAAyE;QACzE,oBAAoB;QACpBL,SAASU,MAAM,CAAC;YACdC,UAAU5C,UAAU6C,WAAW;YAC/BC,SAAS;YACTC,UAAU;YACVC,QAAQ;QACV;IACF;IAEA,MAAMC,UAAU,MAAMtC,oBAAoBsB,UAAU;QAClDiB,YAAY/B;QACZiB;IACF;IAEAH,SAASU,MAAM,CACbJ,YACI;QAACO,SAAS,CAAC,SAAS,EAAEP,WAAW;QAAES,QAAQ;IAAM,IACjD;QACEF,SAASxC;QACTyC,UAAU;QACVC,QAAQ;IACV;IAGN,MAAMG,cAAc,MAAMC,yBAAyB/B,SAAS;QAACoB;QAAQR;IAAQ;IAE7E,MAAMvB,WAAWuB,UAAU;QACzBoB,OAAO,IACL9C,YAAY;gBACV+C,oBAAoBZ;gBACpBa,kBAAkB;gBAClBrB;gBACAX;gBACAiC,QAAQ/B;gBACRU;gBACAC;YACF;QACFqB,YAAYC,sBAAsB;YAACL,OAAO9B,MAAM8B,KAAK;YAAEhB;QAAU;QACjEsB,gBAAgB;IAClB;IAEA,IAAI,CAACtB,YAAY;QACf,MAAMxB,gBAAgB;YAACyB;YAAgBL;YAAUR;QAAS;IAC5D;IAEA,iDAAiD;IACjD,IAAIgB,QAAQ;IAEZ,8EAA8E;IAC9E,8EAA8E;IAC9E,IAAI,CAACU,eAAe,CAACF,SAAS;IAE9B,MAAMW,iBAAiB,MAAMC,mBAAmBxC,SAAS;QAACgB;IAAU;IACpE,MAAMyB,WAAW,MAAMC,qBAAqB;QAC1CZ;QACAT;QACAL;QACAhB;QACAuC;QACAX;IACF;IAEA,OAAO;QACLe,iBAAiB;QACjBC,oBAAoBhB;QACpBiB,QAAQ;YAACC,eAAehB,YAAYiB,EAAE;YAAEC,OAAOlB,YAAYkB,KAAK,IAAI;YAAMC,KAAKR;QAAQ;IACzF;AACF;AAEA;;;CAGC,GACD,eAAeV,yBACb/B,OAAyB,EACzB,EAACoB,MAAM,EAAER,QAAQ,EAA6C;IAE9D,MAAM,EAACC,SAAS,EAAEX,KAAK,EAAEY,MAAM,EAAC,GAAGd;IACnC,MAAMgB,aAAa,CAAC,CAACd,MAAMG,QAAQ;IACnC,6EAA6E;IAC7E,MAAM2C,QAAQ9C,MAAM8C,KAAK,EAAEE,UAAUC;IAErC,IAAI/B,QAAQ;QACV,MAAM7B,kBAAkBqB,UAAU;YAChCwC,OAAOpE,SAAS6B;YAChBG;YACAE,WAAWL,UAAUM,GAAG,EAAED;YAC1BmC,YAAYxC,UAAUwC,UAAU;YAChCL;YACAM,SAASpD,MAAM+C,GAAG;QACpB;QACA,OAAO;IACT;IAEA,MAAM/B,YAAYL,UAAUM,GAAG,EAAED,aAAa;IAC9C,IAAIY,cAAc,MAAMjC,6BAA6B;QACnDuD,OAAOpE,SAAS6B;QAChBG;QACAF;QACAI;QACAmC,YAAYxC,UAAUwC,UAAU;QAChCL;QACAO,YAAY,CAAC,CAACrD,MAAMsD,GAAG;QACvBF,SAASpD,MAAM+C,GAAG;IACpB;IAEA,IAAI,CAACnB,aAAa;QAChB,IAAId,YAAY;YACdF,OAAO2C,GAAG,CAAC;YACX3C,OAAO2C,GAAG,CAAC;QACb,OAAO;YACL3C,OAAO2C,GAAG,CAAC;YACX3C,OAAO2C,GAAG,CAAC;YACX3C,OAAO2C,GAAG,CAAC;QACb;QAEA3B,cAAc,MAAM3C,4BAA4B;YAC9C+B;YACA8B;YACAU,SAAS1C,aAAa,aAAa;QACrC;QACAvB,YAAY,4BAA4BqC;IAC1C;IAEArC,YAAY,0BAA0BqC;IACtC,OAAOA;AACT;AAEA,kFAAkF,GAClF,eAAeU,mBACbxC,OAAyB,EACzB,EAACgB,UAAU,EAAwB;IAEnC,MAAM,EAACH,SAAS,EAAEX,KAAK,EAAEY,MAAM,EAAEX,WAAW,EAAEC,SAAS,EAAC,GAAGJ;IAE3D,IAAIuC,iBAAwC;IAC5C,IAAI;QACFA,iBAAiB,MAAM3C,gCACrB;YACE+D,YAAYxD,YAAYyD,IAAI;YAC5B5C;YACA6C,SAAS,GAAGzD,UAAU,OAAO,CAAC;YAC9Bc,WAAWL,UAAUM,GAAG,EAAED,aAAa;YACvC4C,gBAAgB5D,KAAK,CAAC,kBAAkB;YACxC6D,SAAS7D,MAAM6D,OAAO;YACtBhD,SAASZ,YAAYK,SAAS;QAChC,GACAM;IAEJ,EAAE,OAAOkD,OAAO;QACdvE,YAAY,gDAAgDuE;QAC5D,IAAIA,iBAAiBtF,uBAAuB;YAC1CoC,OAAOkD,KAAK,CAACvF,uBAAuBuF,MAAMC,UAAU,IAAI,EAAE,GAAG;gBAACC,MAAM;YAAC;QACvE;QACApD,OAAOkD,KAAK,CAAC,CAAC,8CAA8C,EAAEA,OAAO,EAAE;YAACE,MAAM;QAAC;IACjF;IAEA,IAAI,CAAC3B,gBAAgB;QACnBzB,OAAOkD,KAAK,CAAC,gFAAgF;YAC3FE,MAAM;QACR;IACF;IAEA,OAAO3B;AACT;AAEA,eAAeG,qBAAqB,EAClCZ,WAAW,EACXT,cAAc,EACdL,UAAU,EACVhB,OAAO,EACPuC,cAAc,EACdX,OAAO,EAQR;IACC,MAAM,EAACf,SAAS,EAAEC,MAAM,EAAEV,SAAS,EAAC,GAAGJ;IAEvC,IAAImE;IACJ,IAAI,CAACnD,YAAY;QACfmD,UAAUrF,KAAKR,QAAQ8B,YAAY;YAACgE,SAAS;gBAAC/F,SAAS+B;aAAW;QAAA,GAAGiE,IAAI,CAAC7F;IAC5E;IAEA,MAAM8F,OAAO1F,QAAQoC,aAAa,uBAAuB,8BAA8BuD,KAAK;IAC5F,IAAI9B;IACJ,IAAI;;QACA,CAAA,EAACA,QAAQ,EAAC,GAAG,MAAM1D,iBAAiB;YACpC+D,eAAehB,YAAYiB,EAAE;YAC7ByB,OAAO;YACPnD;YACAoD,UAAUlC;YACVrB,WAAWL,UAAUM,GAAG,EAAED;YAC1BiD;YACAvC;QACF,EAAC;IACH,EAAE,OAAOoC,OAAO;QACdM,KAAKI,IAAI;QACT,MAAMV;IACR;IACAM,KAAKK,OAAO;IAEZ,MAAMC,QAAQ9C,YAAYkB,KAAK,GAAG,CAAC,IAAI,EAAElB,YAAYkB,KAAK,CAAC,CAAC,CAAC,GAAG;IAChElC,OAAO2C,GAAG,CACRzC,aACI,CAAC,4BAA4B,EAAE4D,OAAO,GACtC,CAAC,8BAA8B,EAAErG,UAAU,QAAQkE,YAAYmC,OAAO;IAG5E,IAAI5F,SAAS6B,YAAY,OAAO4B;IAEhC,MAAMoC,UAAU,CAAC;;;;IAIf,EAAEtG,UAAU,QAAQ,CAAC,QAAQ,EAAEuD,YAAYiB,EAAE,CAAC,CAAC,CAAC,EAAE;;;EAGpD,CAAC;IACDjC,OAAO2C,GAAG,CAAC,CAAC,MAAM,EAAElF,UAAU,QAAQ,CAAC,QAAQ,EAAEuD,YAAYiB,EAAE,CAAC,CAAC,CAAC,GAAG;IACrEjC,OAAO2C,GAAG,CAAC,CAAC,+DAA+D,CAAC;IAC5E3C,OAAO2C,GAAG,CAAC,CAAC,qDAAqD,CAAC;IAClE3C,OAAO2C,GAAG,CAAC,CAAC,EAAE,EAAEoB,SAAS;IAEzB,OAAOpC;AACT;AAEA,SAASJ,sBAAsB,EAACL,KAAK,EAAEhB,UAAU,EAAwC;IACvF,IAAIA,YAAY,OAAO;IACvB,IAAI,CAACgB,OAAO,OAAO;IACnB;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/deployStudio.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip, type Gzip} from 'node:zlib'\n\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {\n type BrettWorkspace,\n buildExposes,\n deployStudio as deployWorkbenchStudio,\n getApplicationUrl,\n getWorkbench,\n} from '@sanity/workbench-cli/deploy'\nimport {type StudioManifest} from 'sanity'\nimport {pack} from 'tar-fs'\n\nimport {createDeployment, type UserApplication} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_ORGANIZATION_ID, NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {buildStudio} from '../build/buildStudio.js'\nimport {createStudioUserApplication} from './createUserApplication.js'\nimport {\n checkAutoUpdates,\n checkBuild,\n checkPackageVersion,\n checkStudioTarget,\n type DeployCheckReporter,\n verifyOutputDir,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {listDeploymentFiles, reportExposes} from './deploymentPlan.js'\nimport {type DeployResult, runDeploy} from './deployRunner.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\n\nconst STUDIO_PACKAGE = 'sanity'\n\nexport function deployStudio(options: DeployAppOptions): Promise<void> {\n return runDeploy(options, {\n listFiles: ({flags, projectRoot, sourceDir}) =>\n flags.external ? Promise.resolve([]) : listDeploymentFiles(sourceDir, projectRoot.directory),\n run: runStudioDeployment,\n type: 'studio',\n })\n}\n\n/** Validates the deploy, extracts and uploads the schema, and ships the build. */\nasync function runStudioDeployment(\n options: DeployAppOptions,\n reporter: DeployCheckReporter,\n): Promise<DeployResult | void> {\n const {cliConfig, flags, output, sourceDir} = options\n const workDir = options.projectRoot.directory\n const isExternal = !!flags.external\n const workbench = getWorkbench(cliConfig)\n const isWorkbenchApp = workbench !== null\n const projectId = cliConfig.api?.projectId\n const organizationId = cliConfig.app?.organizationId\n const appId = getAppId(cliConfig)\n const dryRun = !!flags['dry-run']\n\n // A federated app deploys through Sanity's build/hosting pipeline, which\n // --external skips — fail before doing any other work.\n if (isExternal && isWorkbenchApp) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'Deploying a federated application to an external host is not yet supported',\n solution: 'Remove the --external flag to deploy to Sanity hosting',\n status: 'fail',\n })\n }\n\n const appTitle = workbench\n ? flags.title?.trim() || cliConfig.app?.title?.trim() || workbench.name\n : ''\n\n const isAutoUpdating = checkAutoUpdates(reporter, {cliConfig, flags})\n\n const version = await checkPackageVersion(reporter, {\n moduleName: STUDIO_PACKAGE,\n workDir,\n })\n\n reporter.report(\n projectId\n ? {message: `Project: ${projectId}`, status: 'pass'}\n : {\n message: NO_PROJECT_ID,\n solution: 'Add `api.projectId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n\n // Workbench studios deploy to Brett (which needs the org); plain studios\n // resolve/create on user-applications, unchanged.\n let application: UserApplication | null = null\n let studioCreated = false\n if (workbench && !isExternal) {\n reporter.report(\n organizationId\n ? {message: `Organization: ${organizationId}`, status: 'pass'}\n : {\n message: NO_ORGANIZATION_ID,\n solution: 'Add `app.organizationId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n await checkStudioTarget(reporter, {\n appId,\n isWorkbenchApp: true,\n studioHost: cliConfig.studioHost,\n title: appTitle,\n })\n } else {\n ;({application, created: studioCreated} = await resolveStudioApplication(options, {\n dryRun,\n reporter,\n }))\n }\n\n await checkBuild(reporter, {\n build: () =>\n buildStudio({\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n }),\n skipReason: studioBuildSkipReason({build: flags.build, isExternal}),\n successMessage: 'Studio built',\n })\n\n if (!isExternal) {\n await verifyOutputDir({isWorkbenchApp, reporter, sourceDir})\n }\n\n // Report the exposes deploying with the studio, both modes. External studios\n // host their own bundle, so nothing registers.\n const exposes = workbench && !isExternal ? reportExposes(reporter, workbench) : []\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return\n\n // A real deploy has already exited if anything failed; landing here without a\n // resolved version means the deploy target was never resolved.\n if (!version) return\n\n const studioManifest = await uploadStudioSchema(options, {isExternal})\n // Workbench studios deploy to Brett; plain studios use user-applications.\n if (workbench && !isExternal && organizationId) {\n const {applicationId} = await deployWorkbenchStudio({\n appId,\n interfaces: buildExposes(workbench, {\n appName: workbench.name,\n appTitle,\n exposesAppView: true,\n version,\n }),\n isAutoUpdating,\n organizationId,\n output,\n projectId,\n sourceDir,\n studioHost: cliConfig.studioHost,\n title: appTitle,\n version,\n workspaces: toWorkspaces(studioManifest),\n })\n const url = getApplicationUrl({id: applicationId, organizationId, type: 'studio'})\n logWorkbenchStudioDeployed({applicationId, cliConfig, output, url})\n return {\n applicationType: 'studio',\n applicationVersion: version,\n ...(exposes.length > 0 ? {exposes} : {}),\n target: {\n action: appId ? 'update' : 'create',\n applicationId,\n title: appTitle,\n url,\n },\n }\n }\n\n if (!application) return\n const location = await shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n })\n\n return {\n applicationType: 'studio',\n applicationVersion: version,\n target: {\n action: studioCreated ? 'create' : 'update',\n applicationId: application.id,\n title: application.title ?? null,\n url: location,\n },\n }\n}\n\n/**\n * Finds the application a real deploy targets, registering a studio host when\n * none is configured. A dry run resolves and reports the target read-only instead.\n */\nasync function resolveStudioApplication(\n options: DeployAppOptions,\n {dryRun, reporter}: {dryRun: boolean; reporter: DeployCheckReporter},\n): Promise<{application: UserApplication | null; created: boolean}> {\n const {cliConfig, flags, output} = options\n const isExternal = !!flags.external\n const appId = getAppId(cliConfig)\n // Sets the title on a newly registered studio; blank falls back to undefined\n const title = flags.title?.trim() || undefined\n\n if (dryRun) {\n await checkStudioTarget(reporter, {\n appId,\n isExternal,\n projectId: cliConfig.api?.projectId,\n studioHost: cliConfig.studioHost,\n title,\n urlFlag: flags.url,\n })\n return {application: null, created: false}\n }\n\n const projectId = cliConfig.api?.projectId ?? ''\n // `created` is true when a configured-but-unregistered host was just registered.\n const {application, created} = await findUserApplicationForStudio({\n appId,\n isExternal,\n output,\n projectId,\n studioHost: cliConfig.studioHost,\n title,\n unattended: !!flags.yes,\n urlFlag: flags.url,\n })\n\n if (!application) {\n if (isExternal) {\n output.log('Your project has not been registered with an external studio URL.')\n output.log('Please enter the full URL where your studio is hosted.')\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log('To deploy your Sanity Studio to our hosted sanity.studio service,')\n output.log('you will need one. Please enter the subdomain you want to use.')\n }\n\n const registered = await createStudioUserApplication({\n projectId,\n title,\n urlType: isExternal ? 'external' : 'internal',\n })\n deployDebug('Created user application', registered)\n return {application: registered, created: true}\n }\n\n deployDebug('Found user application', application)\n return {application, created}\n}\n\n/** Extracts the studio schema and manifest and uploads them to the schema store. */\nasync function uploadStudioSchema(\n options: DeployAppOptions,\n {isExternal}: {isExternal: boolean},\n): Promise<StudioManifest | null> {\n const {cliConfig, flags, output, projectRoot, sourceDir} = options\n\n let studioManifest: StudioManifest | null = null\n try {\n studioManifest = await deployStudioSchemasAndManifests(\n {\n configPath: projectRoot.path,\n isExternal,\n outPath: `${sourceDir}/static`,\n projectId: cliConfig.api?.projectId ?? '',\n schemaRequired: flags['schema-required'],\n verbose: flags.verbose,\n workDir: projectRoot.directory,\n },\n output,\n )\n } catch (error) {\n deployDebug('Error deploying studio schemas and manifests', error)\n if (error instanceof SchemaExtractionError) {\n output.error(formatSchemaValidation(error.validation || []), {exit: 1})\n }\n output.error(`Error deploying studio schemas and manifests: ${error}`, {exit: 1})\n }\n\n if (!studioManifest) {\n output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {\n exit: 1,\n })\n }\n\n return studioManifest\n}\n\nasync function shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n}: {\n application: UserApplication\n isAutoUpdating: boolean\n isExternal: boolean\n options: DeployAppOptions\n studioManifest: StudioManifest | null\n version: string\n}): Promise<string> {\n const {cliConfig, output, sourceDir} = options\n\n let tarball: Gzip | undefined\n if (!isExternal) {\n tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n }\n\n const spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start()\n let location: string\n try {\n ;({location} = await createDeployment({\n applicationId: application.id,\n isApp: false,\n isAutoUpdating,\n manifest: studioManifest,\n projectId: cliConfig.api?.projectId,\n tarball,\n version,\n }))\n } catch (error) {\n spin.fail()\n throw error\n }\n spin.succeed()\n\n const named = application.title ? ` — \"${application.title}\"` : ''\n output.log(\n isExternal\n ? `\\nSuccess! Studio registered${named}`\n : `\\nSuccess! Studio deployed to ${styleText('cyan', location)}${named}`,\n )\n\n if (getAppId(cliConfig)) return location\n\n const example = `Example:\nexport default defineCliConfig({\n //…\n deployment: {\n ${styleText('cyan', `appId: '${application.id}'`)},\n },\n //…\n})`\n output.log(`\\nAdd ${styleText('cyan', `appId: '${application.id}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n output.log(`\\n${example}`)\n\n return location\n}\n\nfunction toWorkspaces(manifest: StudioManifest | null): BrettWorkspace[] {\n return (manifest?.workspaces ?? []).map((workspace) => ({\n basePath: workspace.basePath,\n dataset: workspace.dataset,\n icon: workspace.icon,\n name: workspace.name,\n projectId: workspace.projectId,\n subtitle: workspace.subtitle,\n title: workspace.title,\n }))\n}\n\n/** Renders the workbench studio's deploy result; the appId hint shows only when none is configured. */\nfunction logWorkbenchStudioDeployed({\n applicationId,\n cliConfig,\n output,\n url,\n}: {\n applicationId: string\n cliConfig: DeployAppOptions['cliConfig']\n output: DeployAppOptions['output']\n url: string\n}): void {\n output.log(`\\nSuccess! Studio deployed to ${styleText('cyan', url)}`)\n if (getAppId(cliConfig)) return\n\n output.log(`\\nAdd ${styleText('cyan', `appId: '${applicationId}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n}\n\nfunction studioBuildSkipReason({build, isExternal}: {build: boolean; isExternal: boolean}) {\n if (isExternal) return 'Build skipped for externally hosted studios'\n if (!build) return 'Build skipped (--no-build) — validating existing output directory'\n return\n}\n"],"names":["basename","dirname","styleText","createGzip","formatSchemaValidation","SchemaExtractionError","exitCodes","spinner","buildExposes","deployStudio","deployWorkbenchStudio","getApplicationUrl","getWorkbench","pack","createDeployment","getAppId","NO_ORGANIZATION_ID","NO_PROJECT_ID","buildStudio","createStudioUserApplication","checkAutoUpdates","checkBuild","checkPackageVersion","checkStudioTarget","verifyOutputDir","deployDebug","listDeploymentFiles","reportExposes","runDeploy","deployStudioSchemasAndManifests","findUserApplicationForStudio","STUDIO_PACKAGE","options","listFiles","flags","projectRoot","sourceDir","external","Promise","resolve","directory","run","runStudioDeployment","type","reporter","cliConfig","output","workDir","isExternal","workbench","isWorkbenchApp","projectId","api","organizationId","app","appId","dryRun","report","exitCode","USAGE_ERROR","message","solution","status","appTitle","title","trim","name","isAutoUpdating","version","moduleName","application","studioCreated","studioHost","created","resolveStudioApplication","build","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","studioBuildSkipReason","successMessage","exposes","studioManifest","uploadStudioSchema","applicationId","interfaces","appName","exposesAppView","workspaces","toWorkspaces","url","id","logWorkbenchStudioDeployed","applicationType","applicationVersion","length","target","action","location","shipStudioDeployment","undefined","urlFlag","unattended","yes","log","registered","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","exit","tarball","entries","pipe","spin","start","isApp","manifest","fail","succeed","named","example","map","workspace","basePath","dataset","icon","subtitle"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { readdir, stat } from 'node:fs/promises';
2
2
  import { join, relative, sep } from 'node:path';
3
3
  import { styleText } from 'node:util';
4
- import { logSymbols } from '@sanity/cli-core/ux';
4
+ import { summarizeExposes } from '@sanity/workbench-cli/deploy';
5
+ import { checkStatusIcon, nestLines, renderIssues } from '../../util/checks.js';
5
6
  import { pluralize } from '../../util/pluralize.js';
6
7
  /**
7
8
  * Lists the files a deploy would pack from `sourceDir`, as paths relative to
@@ -49,17 +50,43 @@ function totalBytes(files) {
49
50
  if (check.status === 'fail') errors[check.message] = check.solution ?? null;
50
51
  else if (check.status === 'warn') warnings.push(check.message);
51
52
  }
53
+ // `exposes`, `config` and `isSingleton` are workbench-only; plain
54
+ // apps (and apps that don't set the flag) omit them.
52
55
  return {
53
56
  applicationType: plan.type,
54
57
  applicationVersion: plan.version,
55
58
  errors,
59
+ ...plan.exposes.length > 0 ? {
60
+ exposes: plan.exposes
61
+ } : {},
56
62
  files: plan.files,
63
+ ...plan.config ? {
64
+ config: plan.config
65
+ } : {},
57
66
  isDeployable: isDeployable(plan),
67
+ ...plan.isSingleton === undefined ? {} : {
68
+ isSingleton: plan.isSingleton
69
+ },
58
70
  target: plan.target,
59
71
  totalBytes: totalBytes(plan.files),
60
72
  warnings
61
73
  };
62
74
  }
75
+ /**
76
+ * Reports an app's exposes as pass checks and returns them structured for the
77
+ * `--json` payload. The structured list rides on the first check so a dry run's
78
+ * collector can read it back.
79
+ */ export function reportExposes(reporter, app) {
80
+ const { exposes, lines } = summarizeExposes(app);
81
+ for (const [index, message] of lines.entries()){
82
+ reporter.report({
83
+ exposes: index === 0 ? exposes : undefined,
84
+ message,
85
+ status: 'pass'
86
+ });
87
+ }
88
+ return exposes;
89
+ }
63
90
  export function renderDeploymentPlan(plan, output) {
64
91
  const label = plan.type === 'coreApp' ? 'application' : 'studio';
65
92
  const problems = plan.checks.filter((check)=>check.status === 'fail');
@@ -68,7 +95,7 @@ export function renderDeploymentPlan(plan, output) {
68
95
  // Only pass/skip here; problems and warnings render below with their fixes.
69
96
  for (const check of plan.checks){
70
97
  if (check.status === 'pass' || check.status === 'skip') {
71
- output.log(` ${statusIcon(check.status)} ${check.message}`);
98
+ output.log(nestLines(` ${checkStatusIcon(check.status)} ${check.message}`));
72
99
  }
73
100
  }
74
101
  output.log(isDeployable(plan) ? styleText('green', `\nThis ${label} can be deployed.`) : styleText('red', `\nThis ${label} can't be deployed.`));
@@ -82,36 +109,8 @@ export function renderDeploymentPlan(plan, output) {
82
109
  }
83
110
  }
84
111
  }
85
- function renderIssues(output, title, checks) {
86
- if (checks.length === 0) return;
87
- output.log(`\n${title}`);
88
- for (const check of checks){
89
- const fix = check.solution ? `: ${check.solution}` : '';
90
- output.log(` ${statusIcon(check.status)} ${check.message}${fix}`);
91
- }
92
- }
93
112
  function formatMB(bytes) {
94
113
  return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
95
114
  }
96
- function statusIcon(status) {
97
- switch(status){
98
- case 'fail':
99
- {
100
- return logSymbols.error;
101
- }
102
- case 'skip':
103
- {
104
- return logSymbols.info;
105
- }
106
- case 'warn':
107
- {
108
- return logSymbols.warning;
109
- }
110
- default:
111
- {
112
- return logSymbols.success;
113
- }
114
- }
115
- }
116
115
 
117
116
  //# sourceMappingURL=deploymentPlan.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/deploymentPlan.ts"],"sourcesContent":["import {readdir, stat} from 'node:fs/promises'\nimport {join, relative, sep} from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {pluralize} from '../../util/pluralize.js'\nimport {type DeployCheck, type DeployTarget} from './deployChecks.js'\n\nexport interface DeploymentFile {\n /** Path relative to the project root, POSIX-style. */\n path: string\n size: number\n}\n\n/** What a `--dry-run` deploy would do: the real deploy sequence with every mutation gated off. */\nexport interface DeploymentPlan {\n checks: DeployCheck[]\n files: DeploymentFile[]\n /** The resolved deploy target; `null` when the checks can't determine one. */\n target: DeployTarget | null\n type: 'coreApp' | 'studio'\n /** Installed framework version the deploy would use; `null` when not found. */\n version: string | null\n}\n\n/**\n * Lists the files a deploy would pack from `sourceDir`, as paths relative to\n * `fromDir`. A missing directory yields an empty list rather than throwing.\n */\nexport async function listDeploymentFiles(\n sourceDir: string,\n fromDir: string,\n): Promise<DeploymentFile[]> {\n const walk = async (dir: string): Promise<string[]> => {\n let entries\n try {\n entries = await readdir(dir, {withFileTypes: true})\n } catch {\n return []\n }\n const nested = await Promise.all(\n entries.map((entry) => {\n const full = join(dir, entry.name)\n return entry.isDirectory() ? walk(full) : Promise.resolve([full])\n }),\n )\n return nested.flat()\n }\n\n const absolute = await walk(sourceDir)\n const files = await Promise.all(\n absolute.map(async (file) => ({\n // Deploy paths are POSIX-style regardless of the host OS (Windows gives `\\`).\n path: relative(fromDir, file).split(sep).join('/'),\n size: (await stat(file)).size,\n })),\n )\n return files.toSorted((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function isDeployable(plan: DeploymentPlan): boolean {\n return plan.checks.every((check) => check.status !== 'fail')\n}\n\nfunction totalBytes(files: DeploymentFile[]): number {\n return files.reduce((sum, file) => sum + file.size, 0)\n}\n\n/**\n * A problem-focused, machine-readable projection of the plan: blocking problems\n * mapped to their fix, warnings as messages. Derived from the same checks the\n * human report renders (its pass/skip lines are informational and omitted here).\n */\nexport function deploymentPlanToJson(plan: DeploymentPlan): {\n applicationType: DeploymentPlan['type']\n applicationVersion: string | null\n errors: Record<string, string | null>\n files: DeploymentFile[]\n isDeployable: boolean\n target: DeployTarget | null\n totalBytes: number\n warnings: string[]\n} {\n const errors: Record<string, string | null> = {}\n const warnings: string[] = []\n for (const check of plan.checks) {\n if (check.status === 'fail') errors[check.message] = check.solution ?? null\n else if (check.status === 'warn') warnings.push(check.message)\n }\n\n return {\n applicationType: plan.type,\n applicationVersion: plan.version,\n errors,\n files: plan.files,\n isDeployable: isDeployable(plan),\n target: plan.target,\n totalBytes: totalBytes(plan.files),\n warnings,\n }\n}\n\nexport function renderDeploymentPlan(plan: DeploymentPlan, output: Output): void {\n const label = plan.type === 'coreApp' ? 'application' : 'studio'\n const problems = plan.checks.filter((check) => check.status === 'fail')\n const warnings = plan.checks.filter((check) => check.status === 'warn')\n\n output.log('\\nDry run — no changes made.\\n')\n\n // Only pass/skip here; problems and warnings render below with their fixes.\n for (const check of plan.checks) {\n if (check.status === 'pass' || check.status === 'skip') {\n output.log(` ${statusIcon(check.status)} ${check.message}`)\n }\n }\n\n output.log(\n isDeployable(plan)\n ? styleText('green', `\\nThis ${label} can be deployed.`)\n : styleText('red', `\\nThis ${label} can't be deployed.`),\n )\n\n renderIssues(output, 'Problems to fix:', problems)\n renderIssues(output, 'Warnings:', warnings)\n\n // A blocked deploy uploads nothing, so only list files for a deployable plan.\n if (isDeployable(plan) && plan.files.length > 0) {\n output.log(\n `\\nFiles to deploy (${plan.files.length} ${pluralize('file', plan.files.length)}, ${formatMB(totalBytes(plan.files))}):`,\n )\n for (const file of plan.files) {\n output.log(` ${file.path} (${formatMB(file.size)})`)\n }\n }\n}\n\nfunction renderIssues(output: Output, title: string, checks: DeployCheck[]): void {\n if (checks.length === 0) return\n\n output.log(`\\n${title}`)\n for (const check of checks) {\n const fix = check.solution ? `: ${check.solution}` : ''\n output.log(` ${statusIcon(check.status)} ${check.message}${fix}`)\n }\n}\n\nfunction formatMB(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`\n}\n\nfunction statusIcon(status: DeployCheck['status']): string {\n switch (status) {\n case 'fail': {\n return logSymbols.error\n }\n case 'skip': {\n return logSymbols.info\n }\n case 'warn': {\n return logSymbols.warning\n }\n default: {\n return logSymbols.success\n }\n }\n}\n"],"names":["readdir","stat","join","relative","sep","styleText","logSymbols","pluralize","listDeploymentFiles","sourceDir","fromDir","walk","dir","entries","withFileTypes","nested","Promise","all","map","entry","full","name","isDirectory","resolve","flat","absolute","files","file","path","split","size","toSorted","a","b","localeCompare","isDeployable","plan","checks","every","check","status","totalBytes","reduce","sum","deploymentPlanToJson","errors","warnings","message","solution","push","applicationType","type","applicationVersion","version","target","renderDeploymentPlan","output","label","problems","filter","log","statusIcon","renderIssues","length","formatMB","title","fix","bytes","toFixed","error","info","warning","success"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/deploymentPlan.ts"],"sourcesContent":["import {readdir, stat} from 'node:fs/promises'\nimport {join, relative, sep} from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\nimport {type DeployedExpose, summarizeExposes} from '@sanity/workbench-cli/deploy'\n\nimport {checkStatusIcon, nestLines, renderIssues} from '../../util/checks.js'\nimport {pluralize} from '../../util/pluralize.js'\nimport {type DeployCheck, type DeployCheckReporter, type DeployTarget} from './deployChecks.js'\n\nexport interface DeploymentFile {\n /** Path relative to the project root, POSIX-style. */\n path: string\n size: number\n}\n\n/** What a `--dry-run` deploy would do: the real deploy sequence with every mutation gated off. */\nexport interface DeploymentPlan {\n checks: DeployCheck[]\n /** Media-library config summary; `null` unless a config deploys. */\n config: string | null\n /** Workbench views and services registered with the deploy. */\n exposes: DeployedExpose[]\n files: DeploymentFile[]\n /** The resolved deploy target; `null` when the checks can't determine one. */\n target: DeployTarget | null\n type: 'coreApp' | 'studio'\n /** Installed framework version the deploy would use; `null` when not found. */\n version: string | null\n\n /** The app's explicit `isSingleton` flag; `undefined` when the app doesn't set it. */\n isSingleton?: boolean\n}\n\n/**\n * Lists the files a deploy would pack from `sourceDir`, as paths relative to\n * `fromDir`. A missing directory yields an empty list rather than throwing.\n */\nexport async function listDeploymentFiles(\n sourceDir: string,\n fromDir: string,\n): Promise<DeploymentFile[]> {\n const walk = async (dir: string): Promise<string[]> => {\n let entries\n try {\n entries = await readdir(dir, {withFileTypes: true})\n } catch {\n return []\n }\n const nested = await Promise.all(\n entries.map((entry) => {\n const full = join(dir, entry.name)\n return entry.isDirectory() ? walk(full) : Promise.resolve([full])\n }),\n )\n return nested.flat()\n }\n\n const absolute = await walk(sourceDir)\n const files = await Promise.all(\n absolute.map(async (file) => ({\n // Deploy paths are POSIX-style regardless of the host OS (Windows gives `\\`).\n path: relative(fromDir, file).split(sep).join('/'),\n size: (await stat(file)).size,\n })),\n )\n return files.toSorted((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function isDeployable(plan: DeploymentPlan): boolean {\n return plan.checks.every((check) => check.status !== 'fail')\n}\n\nfunction totalBytes(files: DeploymentFile[]): number {\n return files.reduce((sum, file) => sum + file.size, 0)\n}\n\n/**\n * A problem-focused, machine-readable projection of the plan: blocking problems\n * mapped to their fix, warnings as messages. Derived from the same checks the\n * human report renders (its pass/skip lines are informational and omitted here).\n */\nexport function deploymentPlanToJson(plan: DeploymentPlan): {\n applicationType: DeploymentPlan['type']\n applicationVersion: string | null\n config?: string\n errors: Record<string, string | null>\n exposes?: DeployedExpose[]\n files: DeploymentFile[]\n isDeployable: boolean\n isSingleton?: boolean\n target: DeployTarget | null\n totalBytes: number\n warnings: string[]\n} {\n const errors: Record<string, string | null> = {}\n const warnings: string[] = []\n for (const check of plan.checks) {\n if (check.status === 'fail') errors[check.message] = check.solution ?? null\n else if (check.status === 'warn') warnings.push(check.message)\n }\n\n // `exposes`, `config` and `isSingleton` are workbench-only; plain\n // apps (and apps that don't set the flag) omit them.\n return {\n applicationType: plan.type,\n applicationVersion: plan.version,\n errors,\n ...(plan.exposes.length > 0 ? {exposes: plan.exposes} : {}),\n files: plan.files,\n ...(plan.config ? {config: plan.config} : {}),\n isDeployable: isDeployable(plan),\n ...(plan.isSingleton === undefined ? {} : {isSingleton: plan.isSingleton}),\n target: plan.target,\n totalBytes: totalBytes(plan.files),\n warnings,\n }\n}\n\n/**\n * Reports an app's exposes as pass checks and returns them structured for the\n * `--json` payload. The structured list rides on the first check so a dry run's\n * collector can read it back.\n */\nexport function reportExposes(\n reporter: DeployCheckReporter,\n app: Parameters<typeof summarizeExposes>[0],\n): DeployedExpose[] {\n const {exposes, lines} = summarizeExposes(app)\n for (const [index, message] of lines.entries()) {\n reporter.report({exposes: index === 0 ? exposes : undefined, message, status: 'pass'})\n }\n return exposes\n}\n\nexport function renderDeploymentPlan(plan: DeploymentPlan, output: Output): void {\n const label = plan.type === 'coreApp' ? 'application' : 'studio'\n const problems = plan.checks.filter((check) => check.status === 'fail')\n const warnings = plan.checks.filter((check) => check.status === 'warn')\n\n output.log('\\nDry run — no changes made.\\n')\n\n // Only pass/skip here; problems and warnings render below with their fixes.\n for (const check of plan.checks) {\n if (check.status === 'pass' || check.status === 'skip') {\n output.log(nestLines(` ${checkStatusIcon(check.status)} ${check.message}`))\n }\n }\n\n output.log(\n isDeployable(plan)\n ? styleText('green', `\\nThis ${label} can be deployed.`)\n : styleText('red', `\\nThis ${label} can't be deployed.`),\n )\n\n renderIssues(output, 'Problems to fix:', problems)\n renderIssues(output, 'Warnings:', warnings)\n\n // A blocked deploy uploads nothing, so only list files for a deployable plan.\n if (isDeployable(plan) && plan.files.length > 0) {\n output.log(\n `\\nFiles to deploy (${plan.files.length} ${pluralize('file', plan.files.length)}, ${formatMB(totalBytes(plan.files))}):`,\n )\n for (const file of plan.files) {\n output.log(` ${file.path} (${formatMB(file.size)})`)\n }\n }\n}\n\nfunction formatMB(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`\n}\n"],"names":["readdir","stat","join","relative","sep","styleText","summarizeExposes","checkStatusIcon","nestLines","renderIssues","pluralize","listDeploymentFiles","sourceDir","fromDir","walk","dir","entries","withFileTypes","nested","Promise","all","map","entry","full","name","isDirectory","resolve","flat","absolute","files","file","path","split","size","toSorted","a","b","localeCompare","isDeployable","plan","checks","every","check","status","totalBytes","reduce","sum","deploymentPlanToJson","errors","warnings","message","solution","push","applicationType","type","applicationVersion","version","exposes","length","config","isSingleton","undefined","target","reportExposes","reporter","app","lines","index","report","renderDeploymentPlan","output","label","problems","filter","log","formatMB","bytes","toFixed"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,IAAI,QAAO,mBAAkB;AAC9C,SAAQC,IAAI,EAAEC,QAAQ,EAAEC,GAAG,QAAO,YAAW;AAC7C,SAAQC,SAAS,QAAO,YAAW;AAGnC,SAA6BC,gBAAgB,QAAO,+BAA8B;AAElF,SAAQC,eAAe,EAAEC,SAAS,EAAEC,YAAY,QAAO,uBAAsB;AAC7E,SAAQC,SAAS,QAAO,0BAAyB;AA2BjD;;;CAGC,GACD,OAAO,eAAeC,oBACpBC,SAAiB,EACjBC,OAAe;IAEf,MAAMC,OAAO,OAAOC;QAClB,IAAIC;QACJ,IAAI;YACFA,UAAU,MAAMhB,QAAQe,KAAK;gBAACE,eAAe;YAAI;QACnD,EAAE,OAAM;YACN,OAAO,EAAE;QACX;QACA,MAAMC,SAAS,MAAMC,QAAQC,GAAG,CAC9BJ,QAAQK,GAAG,CAAC,CAACC;YACX,MAAMC,OAAOrB,KAAKa,KAAKO,MAAME,IAAI;YACjC,OAAOF,MAAMG,WAAW,KAAKX,KAAKS,QAAQJ,QAAQO,OAAO,CAAC;gBAACH;aAAK;QAClE;QAEF,OAAOL,OAAOS,IAAI;IACpB;IAEA,MAAMC,WAAW,MAAMd,KAAKF;IAC5B,MAAMiB,QAAQ,MAAMV,QAAQC,GAAG,CAC7BQ,SAASP,GAAG,CAAC,OAAOS,OAAU,CAAA;YAC5B,8EAA8E;YAC9EC,MAAM5B,SAASU,SAASiB,MAAME,KAAK,CAAC5B,KAAKF,IAAI,CAAC;YAC9C+B,MAAM,AAAC,CAAA,MAAMhC,KAAK6B,KAAI,EAAGG,IAAI;QAC/B,CAAA;IAEF,OAAOJ,MAAMK,QAAQ,CAAC,CAACC,GAAGC,IAAMD,EAAEJ,IAAI,CAACM,aAAa,CAACD,EAAEL,IAAI;AAC7D;AAEA,OAAO,SAASO,aAAaC,IAAoB;IAC/C,OAAOA,KAAKC,MAAM,CAACC,KAAK,CAAC,CAACC,QAAUA,MAAMC,MAAM,KAAK;AACvD;AAEA,SAASC,WAAWf,KAAuB;IACzC,OAAOA,MAAMgB,MAAM,CAAC,CAACC,KAAKhB,OAASgB,MAAMhB,KAAKG,IAAI,EAAE;AACtD;AAEA;;;;CAIC,GACD,OAAO,SAASc,qBAAqBR,IAAoB;IAavD,MAAMS,SAAwC,CAAC;IAC/C,MAAMC,WAAqB,EAAE;IAC7B,KAAK,MAAMP,SAASH,KAAKC,MAAM,CAAE;QAC/B,IAAIE,MAAMC,MAAM,KAAK,QAAQK,MAAM,CAACN,MAAMQ,OAAO,CAAC,GAAGR,MAAMS,QAAQ,IAAI;aAClE,IAAIT,MAAMC,MAAM,KAAK,QAAQM,SAASG,IAAI,CAACV,MAAMQ,OAAO;IAC/D;IAEA,kEAAkE;IAClE,qDAAqD;IACrD,OAAO;QACLG,iBAAiBd,KAAKe,IAAI;QAC1BC,oBAAoBhB,KAAKiB,OAAO;QAChCR;QACA,GAAIT,KAAKkB,OAAO,CAACC,MAAM,GAAG,IAAI;YAACD,SAASlB,KAAKkB,OAAO;QAAA,IAAI,CAAC,CAAC;QAC1D5B,OAAOU,KAAKV,KAAK;QACjB,GAAIU,KAAKoB,MAAM,GAAG;YAACA,QAAQpB,KAAKoB,MAAM;QAAA,IAAI,CAAC,CAAC;QAC5CrB,cAAcA,aAAaC;QAC3B,GAAIA,KAAKqB,WAAW,KAAKC,YAAY,CAAC,IAAI;YAACD,aAAarB,KAAKqB,WAAW;QAAA,CAAC;QACzEE,QAAQvB,KAAKuB,MAAM;QACnBlB,YAAYA,WAAWL,KAAKV,KAAK;QACjCoB;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASc,cACdC,QAA6B,EAC7BC,GAA2C;IAE3C,MAAM,EAACR,OAAO,EAAES,KAAK,EAAC,GAAG5D,iBAAiB2D;IAC1C,KAAK,MAAM,CAACE,OAAOjB,QAAQ,IAAIgB,MAAMlD,OAAO,GAAI;QAC9CgD,SAASI,MAAM,CAAC;YAACX,SAASU,UAAU,IAAIV,UAAUI;YAAWX;YAASP,QAAQ;QAAM;IACtF;IACA,OAAOc;AACT;AAEA,OAAO,SAASY,qBAAqB9B,IAAoB,EAAE+B,MAAc;IACvE,MAAMC,QAAQhC,KAAKe,IAAI,KAAK,YAAY,gBAAgB;IACxD,MAAMkB,WAAWjC,KAAKC,MAAM,CAACiC,MAAM,CAAC,CAAC/B,QAAUA,MAAMC,MAAM,KAAK;IAChE,MAAMM,WAAWV,KAAKC,MAAM,CAACiC,MAAM,CAAC,CAAC/B,QAAUA,MAAMC,MAAM,KAAK;IAEhE2B,OAAOI,GAAG,CAAC;IAEX,4EAA4E;IAC5E,KAAK,MAAMhC,SAASH,KAAKC,MAAM,CAAE;QAC/B,IAAIE,MAAMC,MAAM,KAAK,UAAUD,MAAMC,MAAM,KAAK,QAAQ;YACtD2B,OAAOI,GAAG,CAAClE,UAAU,CAAC,EAAE,EAAED,gBAAgBmC,MAAMC,MAAM,EAAE,CAAC,EAAED,MAAMQ,OAAO,EAAE;QAC5E;IACF;IAEAoB,OAAOI,GAAG,CACRpC,aAAaC,QACTlC,UAAU,SAAS,CAAC,OAAO,EAAEkE,MAAM,iBAAiB,CAAC,IACrDlE,UAAU,OAAO,CAAC,OAAO,EAAEkE,MAAM,mBAAmB,CAAC;IAG3D9D,aAAa6D,QAAQ,oBAAoBE;IACzC/D,aAAa6D,QAAQ,aAAarB;IAElC,8EAA8E;IAC9E,IAAIX,aAAaC,SAASA,KAAKV,KAAK,CAAC6B,MAAM,GAAG,GAAG;QAC/CY,OAAOI,GAAG,CACR,CAAC,mBAAmB,EAAEnC,KAAKV,KAAK,CAAC6B,MAAM,CAAC,CAAC,EAAEhD,UAAU,QAAQ6B,KAAKV,KAAK,CAAC6B,MAAM,EAAE,EAAE,EAAEiB,SAAS/B,WAAWL,KAAKV,KAAK,GAAG,EAAE,CAAC;QAE1H,KAAK,MAAMC,QAAQS,KAAKV,KAAK,CAAE;YAC7ByC,OAAOI,GAAG,CAAC,CAAC,EAAE,EAAE5C,KAAKC,IAAI,CAAC,EAAE,EAAE4C,SAAS7C,KAAKG,IAAI,EAAE,CAAC,CAAC;QACtD;IACF;AACF;AAEA,SAAS0C,SAASC,KAAa;IAC7B,OAAO,GAAG,AAACA,CAAAA,QAAQ,OAAO,IAAG,EAAGC,OAAO,CAAC,GAAG,GAAG,CAAC;AACjD"}
@@ -2,11 +2,12 @@
2
2
  * Finds (or creates) the user application a deploy targets — the interactive
3
3
  * adapter that turns the resolveDeployTarget verdicts into prompts, creation,
4
4
  * and exits. Dry runs consume the same verdicts read-only (see deployChecks).
5
- */ import { select, Separator, spinner } from '@sanity/cli-core/ux';
5
+ */ import { getErrorMessage } from '@sanity/cli-core/errors';
6
+ import { select, Separator, spinner } from '@sanity/cli-core/ux';
6
7
  import { createUserApplication } from '../../services/userApplications.js';
7
8
  import { getAppId } from '../../util/appId.js';
8
- import { getErrorMessage } from '../../util/getErrorMessage.js';
9
- import { createFailFastReporter, describeAppTarget, describeAppTargetError, describeStudioTarget } from './deployChecks.js';
9
+ import { createFailFastReporter } from '../../util/checks.js';
10
+ import { describeAppTarget, describeAppTargetError, describeStudioTarget } from './deployChecks.js';
10
11
  import { deployDebug } from './deployDebug.js';
11
12
  import { resolveAppDeployTarget, resolveStudioDeployTarget } from './resolveDeployTarget.js';
12
13
  export async function findUserApplication(options) {
@@ -73,31 +74,49 @@ export async function findUserApplicationForStudio(options) {
73
74
  output.error(`Failed to resolve deploy target: ${getErrorMessage(error)}`, {
74
75
  exit: 1
75
76
  });
76
- return null;
77
+ return {
78
+ application: null,
79
+ created: false
80
+ };
77
81
  }
78
82
  if (resolution.type === 'found') {
79
83
  spin.succeed();
80
- return resolution.application;
84
+ return {
85
+ application: resolution.application,
86
+ created: false
87
+ };
81
88
  }
82
- // The configured host isn't registered yet — a deploy registers it without prompting
89
+ // The configured host isn't registered yet — a deploy registers it without
90
+ // prompting, so this returns a newly created studio.
83
91
  if (resolution.type === 'would-create') {
84
92
  spin.succeed();
85
- return createFromConfiguredHost({
93
+ const application = await createFromConfiguredHost({
86
94
  appHost: resolution.appHost,
87
95
  output,
88
96
  projectId,
89
97
  title,
90
98
  urlType
91
99
  });
100
+ return {
101
+ application,
102
+ created: application !== null
103
+ };
92
104
  }
93
105
  if (resolution.type === 'needs-input' && !unattended) {
94
106
  spin.succeed();
95
107
  // Nothing to select from — the caller prompts for a brand new host
96
- if (resolution.existing.length === 0) return null;
97
- return promptForExistingStudio({
98
- existing: resolution.existing,
99
- urlType
100
- });
108
+ if (resolution.existing.length === 0) return {
109
+ application: null,
110
+ created: false
111
+ };
112
+ // A selected existing studio is an update; "new" returns null and the caller registers it.
113
+ return {
114
+ application: await promptForExistingStudio({
115
+ existing: resolution.existing,
116
+ urlType
117
+ }),
118
+ created: false
119
+ };
101
120
  }
102
121
  spin.fail();
103
122
  // 'blocked' diagnoses as a skip (its root cause fails an earlier check), so it
@@ -106,12 +125,18 @@ export async function findUserApplicationForStudio(options) {
106
125
  output.error(resolution.message, {
107
126
  exit: 1
108
127
  });
109
- return null;
128
+ return {
129
+ application: null,
130
+ created: false
131
+ };
110
132
  }
111
133
  createFailFastReporter(output).report(describeStudioTarget(resolution, {
112
134
  isExternal
113
135
  }));
114
- return null;
136
+ return {
137
+ application: null,
138
+ created: false
139
+ };
115
140
  }
116
141
  /**
117
142
  * The host is configured (studioHost or --url) but not registered yet: