@sanity/cli 7.17.0 → 8.0.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 (133) hide show
  1. package/README.md +81 -6
  2. package/dist/SanityHelp.js +1 -35
  3. package/dist/SanityHelp.js.map +1 -1
  4. package/dist/actions/assets/assetFileError.js +9 -0
  5. package/dist/actions/assets/assetFileError.js.map +1 -0
  6. package/dist/actions/assets/uploadAssetFromFile.js +40 -0
  7. package/dist/actions/assets/uploadAssetFromFile.js.map +1 -0
  8. package/dist/actions/assets/uploadAssetWithProgress.js +76 -0
  9. package/dist/actions/assets/uploadAssetWithProgress.js.map +1 -0
  10. package/dist/actions/deploy/deployApp.js +39 -44
  11. package/dist/actions/deploy/deployApp.js.map +1 -1
  12. package/dist/actions/deploy/deployChecks.js +21 -13
  13. package/dist/actions/deploy/deployChecks.js.map +1 -1
  14. package/dist/actions/deploy/deployRunner.js +8 -7
  15. package/dist/actions/deploy/deployRunner.js.map +1 -1
  16. package/dist/actions/deploy/deployStudio.js +55 -35
  17. package/dist/actions/deploy/deployStudio.js.map +1 -1
  18. package/dist/actions/deploy/deploymentPlan.js +17 -29
  19. package/dist/actions/deploy/deploymentPlan.js.map +1 -1
  20. package/dist/actions/deploy/resolveDeployTarget.js +2 -8
  21. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  22. package/dist/actions/dev/servers/getDevServerConfig.js +2 -8
  23. package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
  24. package/dist/actions/dev/servers/startStudioDevServer.js +7 -1
  25. package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
  26. package/dist/actions/init/bootstrapRemoteTemplate.js +2 -2
  27. package/dist/actions/init/bootstrapRemoteTemplate.js.map +1 -1
  28. package/dist/actions/schema/listSchemas.js +1 -1
  29. package/dist/actions/schema/listSchemas.js.map +1 -1
  30. package/dist/actions/tokens/constants.js +2 -2
  31. package/dist/actions/tokens/constants.js.map +1 -1
  32. package/dist/actions/tokens/expiry.js +35 -0
  33. package/dist/actions/tokens/expiry.js.map +1 -0
  34. package/dist/actions/tokens/types.js.map +1 -1
  35. package/dist/actions/tokens/validateRole.js +5 -2
  36. package/dist/actions/tokens/validateRole.js.map +1 -1
  37. package/dist/actions/typegen/count.js +6 -0
  38. package/dist/actions/typegen/count.js.map +1 -0
  39. package/dist/actions/typegen/formatPath.js +5 -0
  40. package/dist/actions/typegen/formatPath.js.map +1 -0
  41. package/dist/actions/typegen/percent.js +8 -0
  42. package/dist/actions/typegen/percent.js.map +1 -0
  43. package/dist/actions/typegen/renderTypegenProgress.js +88 -0
  44. package/dist/actions/typegen/renderTypegenProgress.js.map +1 -0
  45. package/dist/actions/undeploy/adapters.js +6 -9
  46. package/dist/actions/undeploy/adapters.js.map +1 -1
  47. package/dist/actions/undeploy/runUndeploy.js +12 -4
  48. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  49. package/dist/actions/undeploy/undeployPlan.js +6 -14
  50. package/dist/actions/undeploy/undeployPlan.js.map +1 -1
  51. package/dist/commands/assets/upload.js +126 -0
  52. package/dist/commands/assets/upload.js.map +1 -0
  53. package/dist/commands/backups/list.js +1 -1
  54. package/dist/commands/backups/list.js.map +1 -1
  55. package/dist/commands/datasets/copy.js +1 -1
  56. package/dist/commands/datasets/copy.js.map +1 -1
  57. package/dist/commands/login.js +2 -8
  58. package/dist/commands/login.js.map +1 -1
  59. package/dist/commands/mcp/configure.js.map +1 -1
  60. package/dist/commands/migrations/list.js +1 -1
  61. package/dist/commands/migrations/list.js.map +1 -1
  62. package/dist/commands/migrations/run.js +67 -3
  63. package/dist/commands/migrations/run.js.map +1 -1
  64. package/dist/commands/new.js +56 -4
  65. package/dist/commands/new.js.map +1 -1
  66. package/dist/commands/organizations/list.js +1 -1
  67. package/dist/commands/organizations/list.js.map +1 -1
  68. package/dist/commands/projects/list.js +39 -12
  69. package/dist/commands/projects/list.js.map +1 -1
  70. package/dist/commands/projects/unclaimed.js +39 -33
  71. package/dist/commands/projects/unclaimed.js.map +1 -1
  72. package/dist/commands/tokens/create.js +92 -7
  73. package/dist/commands/tokens/create.js.map +1 -1
  74. package/dist/commands/tokens/delete.js +2 -2
  75. package/dist/commands/tokens/delete.js.map +1 -1
  76. package/dist/commands/tokens/list.js +12 -11
  77. package/dist/commands/tokens/list.js.map +1 -1
  78. package/dist/commands/tokens/rotate.js +95 -0
  79. package/dist/commands/tokens/rotate.js.map +1 -0
  80. package/dist/commands/typegen/generate.js +222 -0
  81. package/dist/commands/typegen/generate.js.map +1 -0
  82. package/dist/commands/users/list.js +8 -8
  83. package/dist/commands/users/list.js.map +1 -1
  84. package/dist/exports/_internal.d.ts +29 -0
  85. package/dist/exports/_internal.js +1 -0
  86. package/dist/exports/_internal.js.map +1 -1
  87. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +29 -14
  88. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  89. package/dist/exports/invokeSanityCli/help.js +4 -0
  90. package/dist/exports/invokeSanityCli/help.js.map +1 -1
  91. package/dist/exports/invokeSanityCli/index.js +2 -0
  92. package/dist/exports/invokeSanityCli/index.js.map +1 -1
  93. package/dist/hooks/commandNotFound/topicAliases.js +13 -40
  94. package/dist/hooks/commandNotFound/topicAliases.js.map +1 -1
  95. package/dist/hooks/prerun/setupTelemetry.js +3 -2
  96. package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
  97. package/dist/server/vite/plugin-typegen.js +4 -16
  98. package/dist/server/vite/plugin-typegen.js.map +1 -1
  99. package/dist/services/assets.js +46 -0
  100. package/dist/services/assets.js.map +1 -0
  101. package/dist/services/newInstructions.js +48 -0
  102. package/dist/services/newInstructions.js.map +1 -0
  103. package/dist/services/tokens.js +81 -31
  104. package/dist/services/tokens.js.map +1 -1
  105. package/dist/topicAliases.js +43 -0
  106. package/dist/topicAliases.js.map +1 -1
  107. package/dist/util/parseArguments.js +7 -2
  108. package/dist/util/parseArguments.js.map +1 -1
  109. package/dist/util/readTokenFromStdin.js +17 -0
  110. package/dist/util/readTokenFromStdin.js.map +1 -0
  111. package/dist/util/responsiveTable.js +97 -0
  112. package/dist/util/responsiveTable.js.map +1 -0
  113. package/dist/util/telemetry/commandTelemetry.js +34 -0
  114. package/dist/util/telemetry/commandTelemetry.js.map +1 -0
  115. package/dist/util/unclaimedProjects.js +10 -0
  116. package/dist/util/unclaimedProjects.js.map +1 -1
  117. package/oclif.config.js +2 -4
  118. package/oclif.manifest.json +224 -2
  119. package/package.json +12 -10
  120. package/templates/shopify/components/inputs/ShopifyMetafield.tsx +20 -0
  121. package/templates/shopify/docs/features.md +24 -0
  122. package/templates/shopify/schemaTypes/index.ts +2 -0
  123. package/templates/shopify/schemaTypes/objects/shopify/shopifyCollectionType.ts +18 -1
  124. package/templates/shopify/schemaTypes/objects/shopify/shopifyMetafieldType.ts +47 -0
  125. package/templates/shopify/schemaTypes/objects/shopify/shopifyProductType.ts +17 -0
  126. package/templates/shopify/utils/formatMetafieldValue.ts +30 -0
  127. package/templates/shopify-online-storefront/components/inputs/ShopifyMetafield.tsx +22 -0
  128. package/templates/shopify-online-storefront/schemaTypes/index.ts +4 -0
  129. package/templates/shopify-online-storefront/schemaTypes/objects/shop.ts +16 -0
  130. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyCollection.ts +19 -0
  131. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyMetafield.ts +53 -0
  132. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyProduct.ts +19 -0
  133. package/templates/shopify-online-storefront/utils/formatMetafieldValue.ts +30 -0
@@ -4,7 +4,7 @@ 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 { createStudio, deployWorkbenchApp, getApplicationUrl, getWorkbench } from '@sanity/workbench-cli/deploy';
7
+ import { createStudio, deployWorkbenchApp, getApplicationUrl, getWorkbench, toWorkbenchPayload } 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';
@@ -14,7 +14,7 @@ import { readIconFromPath } from '../manifest/extractCoreAppManifest.js';
14
14
  import { createStudioUserApplication } from './createUserApplication.js';
15
15
  import { checkAutoUpdates, checkBuild, checkPackageVersion, checkStudioTarget, verifyOutputDir } from './deployChecks.js';
16
16
  import { deployDebug } from './deployDebug.js';
17
- import { listDeploymentFiles, reportExposes } from './deploymentPlan.js';
17
+ import { listDeploymentFiles, reportInterfaces } from './deploymentPlan.js';
18
18
  import { runDeploy } from './deployRunner.js';
19
19
  import { deployStudioSchemasAndManifests } from './deployStudioSchemasAndManifests.js';
20
20
  import { findUserApplicationForStudio } from './findUserApplication.js';
@@ -67,6 +67,7 @@ export function deployStudio(options) {
67
67
  // resolve/create on user-applications, unchanged.
68
68
  let application = null;
69
69
  let studioCreated = false;
70
+ let workbenchApp;
70
71
  if (workbench && !isExternal) {
71
72
  reporter.report(organizationId ? {
72
73
  message: `Organization: ${organizationId}`,
@@ -76,13 +77,13 @@ export function deployStudio(options) {
76
77
  solution: 'Add `app.organizationId` to sanity.cli.ts',
77
78
  status: 'fail'
78
79
  });
79
- await checkStudioTarget(reporter, {
80
+ workbenchApp = (await checkStudioTarget(reporter, {
80
81
  appId,
81
82
  isWorkbenchApp: true,
82
83
  organizationId,
83
84
  slug: workbench.slug,
84
85
  title: appTitle
85
- });
86
+ }))?.application;
86
87
  } else {
87
88
  ;
88
89
  ({ application, created: studioCreated } = await resolveStudioApplication(options, {
@@ -108,14 +109,16 @@ export function deployStudio(options) {
108
109
  let applicationCreated = false;
109
110
  let rollbackApp;
110
111
  if (!dryRun && workbench && !isExternal && organizationId && !applicationId) {
111
- ;
112
- ({ applicationId, rollback: rollbackApp } = await createStudio({
112
+ const created = await createStudio({
113
113
  organizationId,
114
114
  projectId,
115
115
  slug: workbench.slug,
116
116
  title: appTitle,
117
117
  visibility: workbench.visibility
118
- }));
118
+ });
119
+ workbenchApp = created.application;
120
+ applicationId = created.application.id;
121
+ rollbackApp = created.rollback;
119
122
  applicationCreated = true;
120
123
  }
121
124
  // A record created above is stranded at its slug (and blocks retries) if any
@@ -145,11 +148,29 @@ export function deployStudio(options) {
145
148
  sourceDir
146
149
  });
147
150
  }
148
- // Report the exposes deploying with the studio, both modes. External studios
149
- // host their own bundle, so nothing registers.
150
- const exposes = workbench && !isExternal ? reportExposes(reporter, workbench) : [];
151
+ // An external studio hosts its own bundle, so nothing registers.
152
+ const interfaces = workbench && !isExternal ? reportInterfaces(reporter, workbench) : null;
153
+ const payload = {
154
+ appId: appId ?? null,
155
+ isAutoUpdating,
156
+ ...organizationId ? {
157
+ organizationId
158
+ } : {},
159
+ ...projectId ? {
160
+ projectId
161
+ } : {},
162
+ type: 'studio',
163
+ version,
164
+ ...toWorkbenchPayload(workbench, {
165
+ interfaces,
166
+ title: appTitle
167
+ })
168
+ };
151
169
  // Dry run stops here — everything below mutates.
152
- if (dryRun) return;
170
+ if (dryRun) return {
171
+ application: null,
172
+ payload
173
+ };
153
174
  // A real deploy has already exited if anything failed; landing here without a
154
175
  // resolved version means the deploy target was never resolved.
155
176
  if (!version) return;
@@ -160,6 +181,7 @@ export function deployStudio(options) {
160
181
  // build, so this only ships the deployment; plain studios use user-applications.
161
182
  if (workbench && !isExternal && organizationId && applicationId) {
162
183
  await deployWorkbenchApp({
184
+ access: toAccess(studioManifest),
163
185
  app: cliConfig.app,
164
186
  applicationId,
165
187
  icon: appIcon,
@@ -189,20 +211,10 @@ export function deployStudio(options) {
189
211
  url
190
212
  });
191
213
  return {
192
- applicationType: 'studio',
193
- applicationVersion: version,
194
- ...exposes.length > 0 ? {
195
- exposes
196
- } : {},
197
- target: {
198
- action: applicationCreated ? 'create' : 'update',
199
- applicationId,
200
- ...applicationCreated ? {
201
- slug: workbench.slug
202
- } : {},
203
- title: appTitle,
204
- url
205
- }
214
+ action: applicationCreated ? 'create' : 'update',
215
+ application: workbenchApp ?? null,
216
+ payload,
217
+ url
206
218
  };
207
219
  }
208
220
  if (!application) return;
@@ -215,14 +227,10 @@ export function deployStudio(options) {
215
227
  version
216
228
  });
217
229
  return {
218
- applicationType: 'studio',
219
- applicationVersion: version,
220
- target: {
221
- action: studioCreated ? 'create' : 'update',
222
- applicationId: application.id,
223
- title: application.title ?? null,
224
- url: location
225
- }
230
+ action: studioCreated ? 'create' : 'update',
231
+ application,
232
+ payload,
233
+ url: location
226
234
  };
227
235
  } catch (err) {
228
236
  await rollbackApp?.();
@@ -305,8 +313,8 @@ export function deployStudio(options) {
305
313
  }, output);
306
314
  } catch (error) {
307
315
  deployDebug('Error deploying studio schemas and manifests', error);
308
- if (error instanceof SchemaExtractionError) {
309
- output.error(formatSchemaValidation(error.validation || []), {
316
+ if (error instanceof SchemaExtractionError && error.validation?.length) {
317
+ output.error(formatSchemaValidation(error.validation), {
310
318
  exit: 1
311
319
  });
312
320
  }
@@ -366,6 +374,18 @@ export default defineCliConfig({
366
374
  output.log(`\n${example}`);
367
375
  return location;
368
376
  }
377
+ /**
378
+ * One `datasets` access entry per unique workspace dataset, with a `resourceId`
379
+ * of `"<projectId>.<dataset>"`. Deduped, since workspaces can share a dataset.
380
+ */ function toAccess(manifest) {
381
+ const resourceIds = new Set((manifest?.workspaces ?? []).map((w)=>`${w.projectId}.${w.dataset}`));
382
+ return [
383
+ ...resourceIds
384
+ ].map((resourceId)=>({
385
+ resourceId,
386
+ resourceType: 'dataset'
387
+ }));
388
+ }
369
389
  function toWorkspaces(manifest) {
370
390
  return (manifest?.workspaces ?? []).map((workspace)=>({
371
391
  basePath: workspace.basePath,
@@ -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 {\n type BrettWorkspace,\n createStudio,\n deployWorkbenchApp,\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 {readIconFromPath} from '../manifest/extractCoreAppManifest.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.slug\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 organizationId,\n slug: workbench.slug,\n title: appTitle,\n })\n } else {\n ;({application, created: studioCreated} = await resolveStudioApplication(options, {\n dryRun,\n reporter,\n }))\n }\n\n // A first deploy mints the app id and the build inlines it; --no-build would\n // ship an existing bundle carrying a different id, so it can't be a first deploy.\n if (workbench && !isExternal && !appId && !flags.build) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'A first deploy cannot skip the build (--no-build)',\n solution: 'Drop --no-build so the new application id is inlined into the build',\n status: 'fail',\n })\n }\n\n // Read up front so a bad icon path fails before we create or build.\n const appIcon =\n !dryRun && !isExternal && workbench?.icon\n ? await readIconFromPath(workDir, workbench.icon)\n : undefined\n\n // Create the studio before the build so the bundle carries its real id. A\n // redeploy already has it from `deployment.appId`; a dry run skips creation.\n let applicationId = appId\n let applicationCreated = false\n let rollbackApp: (() => Promise<void>) | undefined\n if (!dryRun && workbench && !isExternal && organizationId && !applicationId) {\n ;({applicationId, rollback: rollbackApp} = await createStudio({\n organizationId,\n projectId,\n slug: workbench.slug,\n title: appTitle,\n visibility: workbench.visibility,\n }))\n applicationCreated = true\n }\n\n // A record created above is stranded at its slug (and blocks retries) if any\n // step before it fully deploys fails, so undo the creation on failure.\n try {\n await checkBuild(reporter, {\n build: () =>\n buildStudio({\n applicationId: workbench ? applicationId : undefined,\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 // The studio was created (or resolved from `deployment.appId`) before the\n // build, so this only ships the deployment; plain studios use user-applications.\n if (workbench && !isExternal && organizationId && applicationId) {\n await deployWorkbenchApp({\n app: cliConfig.app,\n applicationId,\n icon: appIcon,\n isApp: false,\n isAutoUpdating,\n label: 'Deploying studio',\n // Once the deployment is live, a metadata-sync failure must not delete\n // the studio.\n onDeployed: () => {\n rollbackApp = undefined\n },\n sourceDir,\n title: appTitle,\n version,\n visibility: workbench.visibility,\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: applicationCreated ? 'create' : 'update',\n applicationId,\n ...(applicationCreated ? {slug: workbench.slug} : {}),\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 } catch (err) {\n await rollbackApp?.()\n throw err\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 schemaDescriptorId: workspace.schemaDescriptorId,\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","createStudio","deployWorkbenchApp","getApplicationUrl","getWorkbench","pack","createDeployment","getAppId","NO_ORGANIZATION_ID","NO_PROJECT_ID","buildStudio","readIconFromPath","createStudioUserApplication","checkAutoUpdates","checkBuild","checkPackageVersion","checkStudioTarget","verifyOutputDir","deployDebug","listDeploymentFiles","reportExposes","runDeploy","deployStudioSchemasAndManifests","findUserApplicationForStudio","STUDIO_PACKAGE","deployStudio","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","slug","isAutoUpdating","version","moduleName","application","studioCreated","created","resolveStudioApplication","build","appIcon","icon","undefined","applicationId","applicationCreated","rollbackApp","rollback","visibility","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","studioBuildSkipReason","successMessage","exposes","studioManifest","uploadStudioSchema","isApp","label","onDeployed","workspaces","toWorkspaces","url","id","logWorkbenchStudioDeployed","applicationType","applicationVersion","length","target","action","location","shipStudioDeployment","err","studioHost","urlFlag","unattended","yes","log","registered","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","exit","tarball","entries","pipe","spin","start","manifest","fail","succeed","named","example","map","workspace","basePath","dataset","name","schemaDescriptorId","subtitle"],"mappings":"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"}
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 BrettAccess,\n type BrettWorkspace,\n createStudio,\n deployWorkbenchApp,\n getApplicationUrl,\n getWorkbench,\n toWorkbenchPayload,\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 {readIconFromPath} from '../manifest/extractCoreAppManifest.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, reportInterfaces} from './deploymentPlan.js'\nimport {type DeployPayload, 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.slug\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 let workbenchApp: object | undefined\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 workbenchApp = (\n await checkStudioTarget(reporter, {\n appId,\n isWorkbenchApp: true,\n organizationId,\n slug: workbench.slug,\n title: appTitle,\n })\n )?.application\n } else {\n ;({application, created: studioCreated} = await resolveStudioApplication(options, {\n dryRun,\n reporter,\n }))\n }\n\n // A first deploy mints the app id and the build inlines it; --no-build would\n // ship an existing bundle carrying a different id, so it can't be a first deploy.\n if (workbench && !isExternal && !appId && !flags.build) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'A first deploy cannot skip the build (--no-build)',\n solution: 'Drop --no-build so the new application id is inlined into the build',\n status: 'fail',\n })\n }\n\n // Read up front so a bad icon path fails before we create or build.\n const appIcon =\n !dryRun && !isExternal && workbench?.icon\n ? await readIconFromPath(workDir, workbench.icon)\n : undefined\n\n // Create the studio before the build so the bundle carries its real id. A\n // redeploy already has it from `deployment.appId`; a dry run skips creation.\n let applicationId = appId\n let applicationCreated = false\n let rollbackApp: (() => Promise<void>) | undefined\n if (!dryRun && workbench && !isExternal && organizationId && !applicationId) {\n const created = await createStudio({\n organizationId,\n projectId,\n slug: workbench.slug,\n title: appTitle,\n visibility: workbench.visibility,\n })\n workbenchApp = created.application\n applicationId = created.application.id\n rollbackApp = created.rollback\n applicationCreated = true\n }\n\n // A record created above is stranded at its slug (and blocks retries) if any\n // step before it fully deploys fails, so undo the creation on failure.\n try {\n await checkBuild(reporter, {\n build: () =>\n buildStudio({\n applicationId: workbench ? applicationId : undefined,\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 // An external studio hosts its own bundle, so nothing registers.\n const interfaces = workbench && !isExternal ? reportInterfaces(reporter, workbench) : null\n\n const payload: DeployPayload = {\n appId: appId ?? null,\n isAutoUpdating,\n ...(organizationId ? {organizationId} : {}),\n ...(projectId ? {projectId} : {}),\n type: 'studio',\n version,\n ...toWorkbenchPayload(workbench, {interfaces, title: appTitle}),\n }\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return {application: null, payload}\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 // The studio was created (or resolved from `deployment.appId`) before the\n // build, so this only ships the deployment; plain studios use user-applications.\n if (workbench && !isExternal && organizationId && applicationId) {\n await deployWorkbenchApp({\n access: toAccess(studioManifest),\n app: cliConfig.app,\n applicationId,\n icon: appIcon,\n isApp: false,\n isAutoUpdating,\n label: 'Deploying studio',\n // Once the deployment is live, a metadata-sync failure must not delete\n // the studio.\n onDeployed: () => {\n rollbackApp = undefined\n },\n sourceDir,\n title: appTitle,\n version,\n visibility: workbench.visibility,\n workspaces: toWorkspaces(studioManifest),\n })\n const url = getApplicationUrl({id: applicationId, organizationId, type: 'studio'})\n logWorkbenchStudioDeployed({applicationId, cliConfig, output, url})\n return {\n action: applicationCreated ? 'create' : 'update',\n application: workbenchApp ?? null,\n payload,\n url,\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 action: studioCreated ? 'create' : 'update',\n application,\n payload,\n url: location,\n }\n } catch (err) {\n await rollbackApp?.()\n throw err\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 && error.validation?.length) {\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\n/**\n * One `datasets` access entry per unique workspace dataset, with a `resourceId`\n * of `\"<projectId>.<dataset>\"`. Deduped, since workspaces can share a dataset.\n */\nfunction toAccess(manifest: StudioManifest | null): BrettAccess[] {\n const resourceIds = new Set(\n (manifest?.workspaces ?? []).map((w) => `${w.projectId}.${w.dataset}`),\n )\n return [...resourceIds].map(\n (resourceId) => ({resourceId, resourceType: 'dataset'}) satisfies BrettAccess,\n )\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 schemaDescriptorId: workspace.schemaDescriptorId,\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","createStudio","deployWorkbenchApp","getApplicationUrl","getWorkbench","toWorkbenchPayload","pack","createDeployment","getAppId","NO_ORGANIZATION_ID","NO_PROJECT_ID","buildStudio","readIconFromPath","createStudioUserApplication","checkAutoUpdates","checkBuild","checkPackageVersion","checkStudioTarget","verifyOutputDir","deployDebug","listDeploymentFiles","reportInterfaces","runDeploy","deployStudioSchemasAndManifests","findUserApplicationForStudio","STUDIO_PACKAGE","deployStudio","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","slug","isAutoUpdating","version","moduleName","application","studioCreated","workbenchApp","created","resolveStudioApplication","build","appIcon","icon","undefined","applicationId","applicationCreated","rollbackApp","visibility","id","rollback","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","studioBuildSkipReason","successMessage","interfaces","payload","studioManifest","uploadStudioSchema","access","toAccess","isApp","label","onDeployed","workspaces","toWorkspaces","url","logWorkbenchStudioDeployed","action","location","shipStudioDeployment","err","studioHost","urlFlag","unattended","yes","log","registered","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","length","exit","tarball","entries","pipe","spin","start","manifest","fail","succeed","named","example","resourceIds","Set","map","w","dataset","resourceId","resourceType","workspace","basePath","name","schemaDescriptorId","subtitle"],"mappings":"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"}
@@ -50,42 +50,30 @@ function totalBytes(files) {
50
50
  if (check.status === 'fail') errors[check.message] = check.solution ?? null;
51
51
  else if (check.status === 'warn') warnings.push(check.message);
52
52
  }
53
- // `exposes`, `config` and `isSingleton` are workbench-only; plain
54
- // apps (and apps that don't set the flag) omit them.
55
53
  return {
56
- applicationType: plan.type,
57
- applicationVersion: plan.version,
54
+ action: plan.target?.action ?? null,
55
+ // Only a successful deploy reports the backend's record.
56
+ application: null,
57
+ canDeploy: isDeployable(plan),
58
58
  errors,
59
- ...plan.exposes.length > 0 ? {
60
- exposes: plan.exposes
61
- } : {},
62
59
  files: plan.files,
63
- ...plan.config ? {
64
- config: plan.config
65
- } : {},
66
- isDeployable: isDeployable(plan),
67
- ...plan.isSingleton === undefined ? {} : {
68
- isSingleton: plan.isSingleton
69
- },
70
- target: plan.target,
60
+ payload: plan.payload,
61
+ reason: plan.checks.find((check)=>check.status === 'fail')?.message ?? null,
71
62
  totalBytes: totalBytes(plan.files),
63
+ url: plan.target?.url ?? null,
72
64
  warnings
73
65
  };
74
66
  }
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 } = summarizeInterfaces(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;
67
+ export function reportInterfaces(reporter, app) {
68
+ const { lines, services, views } = summarizeInterfaces(app);
69
+ for (const message of lines)reporter.report({
70
+ message,
71
+ status: 'pass'
72
+ });
73
+ return {
74
+ services,
75
+ views
76
+ };
89
77
  }
90
78
  export function renderDeploymentPlan(plan, output) {
91
79
  const label = plan.type === 'coreApp' ? 'application' : 'studio';
@@ -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 {type DeployedExpose, summarizeInterfaces} 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 summarizeInterfaces>[0],\n): DeployedExpose[] {\n const {exposes, lines} = summarizeInterfaces(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","summarizeInterfaces","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":"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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 DeployedInterface, summarizeInterfaces} 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'\nimport {type DeployPayload} from './deployRunner.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 /** `null` when the run bailed before collecting it. */\n payload: DeployPayload | null\n /** The resolved deploy target; `null` when the checks can't determine one. */\n target: DeployTarget | null\n type: 'coreApp' | 'studio'\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 action: DeployTarget['action'] | null\n application: null\n canDeploy: boolean\n errors: Record<string, string | null>\n files: DeploymentFile[]\n payload: DeployPayload | null\n reason: string | null\n totalBytes: number\n url: string | null\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 action: plan.target?.action ?? null,\n // Only a successful deploy reports the backend's record.\n application: null,\n canDeploy: isDeployable(plan),\n errors,\n files: plan.files,\n payload: plan.payload,\n reason: plan.checks.find((check) => check.status === 'fail')?.message ?? null,\n totalBytes: totalBytes(plan.files),\n url: plan.target?.url ?? null,\n warnings,\n }\n}\n\nexport function reportInterfaces(\n reporter: DeployCheckReporter,\n app: Parameters<typeof summarizeInterfaces>[0],\n): {services: DeployedInterface[]; views: DeployedInterface[]} {\n const {lines, services, views} = summarizeInterfaces(app)\n for (const message of lines) reporter.report({message, status: 'pass'})\n return {services, views}\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","summarizeInterfaces","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","action","target","application","canDeploy","payload","reason","find","url","reportInterfaces","reporter","app","lines","services","views","report","renderDeploymentPlan","output","label","type","problems","filter","log","length","formatMB","bytes","toFixed"],"mappings":"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"}
@@ -152,10 +152,7 @@ import { normalizeUrl, validateUrl } from './urlUtils.js';
152
152
  return {
153
153
  appHost: slug,
154
154
  existing: {
155
- appHost: slug,
156
- id: existing.id,
157
- organizationId: existing.organizationId,
158
- title: existing.title,
155
+ ...existing,
159
156
  url: getApplicationUrl(existing)
160
157
  },
161
158
  type: 'slug-taken'
@@ -182,10 +179,7 @@ async function resolveAppById(appId) {
182
179
  const application = await getApplication(appId);
183
180
  return application ? {
184
181
  application: {
185
- appHost: application.slug ?? '',
186
- id: application.id,
187
- organizationId: application.organizationId,
188
- title: application.title,
182
+ ...application,
189
183
  url: getApplicationUrl(application)
190
184
  },
191
185
  type: 'found'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/resolveDeployTarget.ts"],"sourcesContent":["import {getApplication, getApplicationUrl, listApplications} from '@sanity/workbench-cli/deploy'\n\nimport {\n getUserApplication,\n getUserApplications,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {APP_ID_NOT_FOUND_IN_ORGANIZATION} from '../../util/errorMessages.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\n/** The application fields a deploy-target verdict carries for reporting. */\nexport interface DeployTargetApp {\n appHost: string\n id: string\n title: string | null\n\n url?: string\n}\n\n/** A coreApp verdict also carries the organization, for the dashboard URL. */\ninterface DeployTargetCoreApp extends DeployTargetApp {\n organizationId: string\n}\n\n/**\n * The read-only outcome of resolving where a deploy would go.\n *\n * - `found` — the deploy targets this existing application\n * - `would-create` — nothing registered yet; a deploy would create it without prompting\n * - `needs-input` — config doesn't determine a target; a deploy would prompt\n * (`existing` lists the applications a prompt would offer)\n * - `invalid` — the configured target can never resolve (bad host, unknown appId)\n * - `blocked` — resolution requires config that's missing (projectId/organizationId)\n *\n * Transport errors (network, permissions) throw — they're not verdicts.\n *\n * The user-applications resolvers carry the full {@link UserApplication} (the\n * real deploy needs it); the workbench resolvers and the report only need\n * {@link DeployTargetApp}, so `App` defaults to that widened shape.\n */\ntype CommonDeployTargetResolution<App = DeployTargetApp> =\n | {application: App; type: 'found'}\n | {existing: App[]; type: 'needs-input'}\n | {message: string; reason: 'app-not-found' | 'invalid-host'; type: 'invalid'}\n | {message: string; type: 'blocked'}\n\nexport type StudioDeployTargetResolution<App = DeployTargetApp> =\n | CommonDeployTargetResolution<App>\n | {appHost: string; existing: App; type: 'slug-taken'}\n | {appHost: string; type: 'would-create'}\n\nexport type AppDeployTargetResolution<App = DeployTargetCoreApp> =\n | CommonDeployTargetResolution<App>\n | {appHost: string; existing: App; type: 'slug-taken'}\n | {appHost?: string; type: 'would-create'}\n\n/**\n * Owns the studio deploy-target rules: the --url flag over studioHost config,\n * appId over appHost precedence, external URL normalization and validation.\n * Both the real deploy and the dry run consume these verdicts.\n *\n * @internal\n */\nexport async function resolveStudioDeployTarget(options: {\n appId: string | undefined\n isExternal: boolean\n projectId: string | undefined\n studioHost: string | undefined\n urlFlag: string | undefined\n}): Promise<StudioDeployTargetResolution<UserApplication>> {\n const {appId, isExternal, projectId, studioHost, urlFlag} = options\n\n // appId wins over host config (and undeploy resolves it the same way): a\n // configured appId deploys even when studioHost is stale or invalid, so it's\n // resolved before any host validation.\n if (appId) {\n if (!projectId) {\n return {message: 'api.projectId is missing', type: 'blocked'}\n }\n const application = await getUserApplication({appId, isSdkApp: false, projectId})\n if (application) {\n return {application, type: 'found'}\n }\n return {\n message: `Cannot find app with app ID ${appId}`,\n reason: 'app-not-found',\n type: 'invalid',\n }\n }\n\n const {error: hostError, host: resolvedHost} = resolveAppHost({\n isExternal,\n studioHost,\n url: urlFlag,\n })\n if (hostError) {\n return {message: hostError, reason: 'invalid-host', type: 'invalid'}\n }\n\n // A host from config hasn't passed through the --url validation yet\n let appHost = resolvedHost\n if (appHost && isExternal) {\n appHost = normalizeUrl(appHost)\n const validation = validateUrl(appHost)\n if (validation !== true) {\n return {message: validation, reason: 'invalid-host', type: 'invalid'}\n }\n }\n\n if (appHost) {\n if (!projectId) {\n return {message: 'api.projectId is missing', type: 'blocked'}\n }\n const application = await getUserApplication({appHost, isSdkApp: false, projectId})\n if (application) {\n return {application, type: 'found'}\n }\n return {appHost, type: 'would-create'}\n }\n\n // Neither appId nor host configured — a deploy would prompt.\n // Without a project there is nothing to list; a deploy would still prompt.\n const existing = projectId ? await listStudioApplications(projectId, isExternal) : []\n return {existing, type: 'needs-input'}\n}\n\n/**\n * Owns the app deploy-target rules: appId lookup, falling back to listing the\n * organization's applications. Both the real deploy and the dry run consume\n * these verdicts.\n *\n * @internal\n */\nexport async function resolveAppDeployTarget(options: {\n appId: string | undefined\n organizationId: string | undefined\n}): Promise<AppDeployTargetResolution<UserApplicationResolved>> {\n const {appId, organizationId} = options\n\n if (appId) {\n const application = await getUserApplication({appId, isSdkApp: true})\n if (application) {\n return {application, type: 'found'}\n }\n return {\n message: `Cannot find app with app ID ${appId}`,\n reason: 'app-not-found',\n type: 'invalid',\n }\n }\n\n if (!organizationId) {\n return {message: 'app.organizationId is missing', type: 'blocked'}\n }\n\n const existing = await getUserApplications({appType: 'coreApp', organizationId})\n if (existing?.length) {\n return {existing, type: 'needs-input'}\n }\n\n return {type: 'would-create'}\n}\n\n/**\n * The dry-run counterpart to a workbench app's create-on-deploy: without an\n * `appId`, an org already holding the slug resolves to that app, so the report\n * can name its id instead of a create that would fail.\n *\n * @internal\n */\nexport async function resolveWorkbenchApp({\n appId,\n organizationId,\n slug,\n}: {\n appId: string | undefined\n organizationId?: string\n slug?: string\n}): Promise<AppDeployTargetResolution> {\n if (appId) return resolveAppById(appId)\n\n if (organizationId && slug) {\n const existing = (await listApplications(organizationId)).find((app) => app.slug === slug)\n if (existing) {\n return {\n appHost: slug,\n existing: {\n appHost: slug,\n id: existing.id,\n organizationId: existing.organizationId,\n title: existing.title,\n url: getApplicationUrl(existing),\n },\n type: 'slug-taken',\n }\n }\n }\n\n return {appHost: slug, type: 'would-create'}\n}\n\n/**\n * {@link resolveWorkbenchApp} narrowed for a studio, whose slug is always configured.\n *\n * @internal\n */\nexport async function resolveWorkbenchStudio(options: {\n appId: string | undefined\n organizationId?: string\n slug: string\n}): Promise<StudioDeployTargetResolution> {\n const resolution = await resolveWorkbenchApp(options)\n return resolution.type === 'would-create'\n ? {appHost: options.slug, type: 'would-create'}\n : resolution\n}\n\nasync function resolveAppById(\n appId: string,\n): Promise<CommonDeployTargetResolution<DeployTargetCoreApp>> {\n const application = await getApplication(appId)\n return application\n ? {\n application: {\n appHost: application.slug ?? '',\n id: application.id,\n organizationId: application.organizationId,\n title: application.title,\n url: getApplicationUrl(application),\n },\n type: 'found',\n }\n : {message: APP_ID_NOT_FOUND_IN_ORGANIZATION, reason: 'app-not-found', type: 'invalid'}\n}\n\nasync function listStudioApplications(\n projectId: string,\n isExternal: boolean,\n): Promise<UserApplication[]> {\n const urlType = isExternal ? 'external' : 'internal'\n const applications = await getUserApplications({appType: 'studio', projectId})\n // External deploys should only see external studios and vice versa\n return applications?.filter((application) => application.urlType === urlType) ?? []\n}\n\nfunction resolveAppHost({\n isExternal,\n studioHost,\n url,\n}: {\n isExternal: boolean\n studioHost: string | undefined\n url: string | undefined\n}): {error?: string; host?: string} {\n if (!url) {\n return {host: studioHost}\n }\n\n if (isExternal) {\n const normalized = normalizeUrl(url)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return {error: validation}\n }\n return {host: normalized}\n }\n\n // For internal deploys, strip protocol prefix and .sanity.studio suffix if present\n const hostname = url.replace(/^https?:\\/\\//i, '').replace(/\\.sanity\\.studio\\/?$/i, '')\n\n // If the result still looks like a URL (contains dots), the user likely meant --external\n if (hostname.includes('.')) {\n return {\n error: `\"${hostname}\" does not look like a sanity.studio hostname. Did you mean to use --external?`,\n }\n }\n\n // Validate hostname characters (alphanumeric and hyphens only)\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(hostname)) {\n return {\n error: `Invalid studio hostname \"${hostname}\". Hostnames can only contain letters, numbers, and hyphens.`,\n }\n }\n\n return {host: hostname}\n}\n"],"names":["getApplication","getApplicationUrl","listApplications","getUserApplication","getUserApplications","APP_ID_NOT_FOUND_IN_ORGANIZATION","normalizeUrl","validateUrl","resolveStudioDeployTarget","options","appId","isExternal","projectId","studioHost","urlFlag","message","type","application","isSdkApp","reason","error","hostError","host","resolvedHost","resolveAppHost","url","appHost","validation","existing","listStudioApplications","resolveAppDeployTarget","organizationId","appType","length","resolveWorkbenchApp","slug","resolveAppById","find","app","id","title","resolveWorkbenchStudio","resolution","urlType","applications","filter","normalized","hostname","replace","includes","test"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/resolveDeployTarget.ts"],"sourcesContent":["import {getApplication, getApplicationUrl, listApplications} from '@sanity/workbench-cli/deploy'\n\nimport {\n getUserApplication,\n getUserApplications,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {APP_ID_NOT_FOUND_IN_ORGANIZATION} from '../../util/errorMessages.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\n/** The application fields a deploy-target verdict carries for reporting. */\nexport interface DeployTargetApp {\n id: string\n title: string | null\n type: 'coreApp' | 'studio'\n\n appHost?: string\n url?: string\n}\n\n/** A coreApp verdict also carries the organization, for the dashboard URL. */\ninterface DeployTargetCoreApp extends DeployTargetApp {\n organizationId: string\n}\n\n/**\n * The read-only outcome of resolving where a deploy would go.\n *\n * - `found` — the deploy targets this existing application\n * - `would-create` — nothing registered yet; a deploy would create it without prompting\n * - `needs-input` — config doesn't determine a target; a deploy would prompt\n * (`existing` lists the applications a prompt would offer)\n * - `invalid` — the configured target can never resolve (bad host, unknown appId)\n * - `blocked` — resolution requires config that's missing (projectId/organizationId)\n *\n * Transport errors (network, permissions) throw — they're not verdicts.\n *\n * The user-applications resolvers carry the full {@link UserApplication} (the\n * real deploy needs it); the workbench resolvers and the report only need\n * {@link DeployTargetApp}, so `App` defaults to that widened shape.\n */\ntype CommonDeployTargetResolution<App = DeployTargetApp> =\n | {application: App; type: 'found'}\n | {existing: App[]; type: 'needs-input'}\n | {message: string; reason: 'app-not-found' | 'invalid-host'; type: 'invalid'}\n | {message: string; type: 'blocked'}\n\nexport type StudioDeployTargetResolution<App = DeployTargetApp> =\n | CommonDeployTargetResolution<App>\n | {appHost: string; existing: App; type: 'slug-taken'}\n | {appHost: string; type: 'would-create'}\n\nexport type AppDeployTargetResolution<App = DeployTargetCoreApp> =\n | CommonDeployTargetResolution<App>\n | {appHost: string; existing: App; type: 'slug-taken'}\n | {appHost?: string; type: 'would-create'}\n\n/**\n * Owns the studio deploy-target rules: the --url flag over studioHost config,\n * appId over appHost precedence, external URL normalization and validation.\n * Both the real deploy and the dry run consume these verdicts.\n *\n * @internal\n */\nexport async function resolveStudioDeployTarget(options: {\n appId: string | undefined\n isExternal: boolean\n projectId: string | undefined\n studioHost: string | undefined\n urlFlag: string | undefined\n}): Promise<StudioDeployTargetResolution<UserApplication>> {\n const {appId, isExternal, projectId, studioHost, urlFlag} = options\n\n // appId wins over host config (and undeploy resolves it the same way): a\n // configured appId deploys even when studioHost is stale or invalid, so it's\n // resolved before any host validation.\n if (appId) {\n if (!projectId) {\n return {message: 'api.projectId is missing', type: 'blocked'}\n }\n const application = await getUserApplication({appId, isSdkApp: false, projectId})\n if (application) {\n return {application, type: 'found'}\n }\n return {\n message: `Cannot find app with app ID ${appId}`,\n reason: 'app-not-found',\n type: 'invalid',\n }\n }\n\n const {error: hostError, host: resolvedHost} = resolveAppHost({\n isExternal,\n studioHost,\n url: urlFlag,\n })\n if (hostError) {\n return {message: hostError, reason: 'invalid-host', type: 'invalid'}\n }\n\n // A host from config hasn't passed through the --url validation yet\n let appHost = resolvedHost\n if (appHost && isExternal) {\n appHost = normalizeUrl(appHost)\n const validation = validateUrl(appHost)\n if (validation !== true) {\n return {message: validation, reason: 'invalid-host', type: 'invalid'}\n }\n }\n\n if (appHost) {\n if (!projectId) {\n return {message: 'api.projectId is missing', type: 'blocked'}\n }\n const application = await getUserApplication({appHost, isSdkApp: false, projectId})\n if (application) {\n return {application, type: 'found'}\n }\n return {appHost, type: 'would-create'}\n }\n\n // Neither appId nor host configured — a deploy would prompt.\n // Without a project there is nothing to list; a deploy would still prompt.\n const existing = projectId ? await listStudioApplications(projectId, isExternal) : []\n return {existing, type: 'needs-input'}\n}\n\n/**\n * Owns the app deploy-target rules: appId lookup, falling back to listing the\n * organization's applications. Both the real deploy and the dry run consume\n * these verdicts.\n *\n * @internal\n */\nexport async function resolveAppDeployTarget(options: {\n appId: string | undefined\n organizationId: string | undefined\n}): Promise<AppDeployTargetResolution<UserApplicationResolved>> {\n const {appId, organizationId} = options\n\n if (appId) {\n const application = await getUserApplication({appId, isSdkApp: true})\n if (application) {\n return {application, type: 'found'}\n }\n return {\n message: `Cannot find app with app ID ${appId}`,\n reason: 'app-not-found',\n type: 'invalid',\n }\n }\n\n if (!organizationId) {\n return {message: 'app.organizationId is missing', type: 'blocked'}\n }\n\n const existing = await getUserApplications({appType: 'coreApp', organizationId})\n if (existing?.length) {\n return {existing, type: 'needs-input'}\n }\n\n return {type: 'would-create'}\n}\n\n/**\n * The dry-run counterpart to a workbench app's create-on-deploy: without an\n * `appId`, an org already holding the slug resolves to that app, so the report\n * can name its id instead of a create that would fail.\n *\n * @internal\n */\nexport async function resolveWorkbenchApp({\n appId,\n organizationId,\n slug,\n}: {\n appId: string | undefined\n organizationId?: string\n slug?: string\n}): Promise<AppDeployTargetResolution> {\n if (appId) return resolveAppById(appId)\n\n if (organizationId && slug) {\n const existing = (await listApplications(organizationId)).find((app) => app.slug === slug)\n if (existing) {\n return {\n appHost: slug,\n existing: {...existing, url: getApplicationUrl(existing)},\n type: 'slug-taken',\n }\n }\n }\n\n return {appHost: slug, type: 'would-create'}\n}\n\n/**\n * {@link resolveWorkbenchApp} narrowed for a studio, whose slug is always configured.\n *\n * @internal\n */\nexport async function resolveWorkbenchStudio(options: {\n appId: string | undefined\n organizationId?: string\n slug: string\n}): Promise<StudioDeployTargetResolution> {\n const resolution = await resolveWorkbenchApp(options)\n return resolution.type === 'would-create'\n ? {appHost: options.slug, type: 'would-create'}\n : resolution\n}\n\nasync function resolveAppById(\n appId: string,\n): Promise<CommonDeployTargetResolution<DeployTargetCoreApp>> {\n const application = await getApplication(appId)\n return application\n ? {\n application: {...application, url: getApplicationUrl(application)},\n type: 'found',\n }\n : {message: APP_ID_NOT_FOUND_IN_ORGANIZATION, reason: 'app-not-found', type: 'invalid'}\n}\n\nasync function listStudioApplications(\n projectId: string,\n isExternal: boolean,\n): Promise<UserApplication[]> {\n const urlType = isExternal ? 'external' : 'internal'\n const applications = await getUserApplications({appType: 'studio', projectId})\n // External deploys should only see external studios and vice versa\n return applications?.filter((application) => application.urlType === urlType) ?? []\n}\n\nfunction resolveAppHost({\n isExternal,\n studioHost,\n url,\n}: {\n isExternal: boolean\n studioHost: string | undefined\n url: string | undefined\n}): {error?: string; host?: string} {\n if (!url) {\n return {host: studioHost}\n }\n\n if (isExternal) {\n const normalized = normalizeUrl(url)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return {error: validation}\n }\n return {host: normalized}\n }\n\n // For internal deploys, strip protocol prefix and .sanity.studio suffix if present\n const hostname = url.replace(/^https?:\\/\\//i, '').replace(/\\.sanity\\.studio\\/?$/i, '')\n\n // If the result still looks like a URL (contains dots), the user likely meant --external\n if (hostname.includes('.')) {\n return {\n error: `\"${hostname}\" does not look like a sanity.studio hostname. Did you mean to use --external?`,\n }\n }\n\n // Validate hostname characters (alphanumeric and hyphens only)\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(hostname)) {\n return {\n error: `Invalid studio hostname \"${hostname}\". Hostnames can only contain letters, numbers, and hyphens.`,\n }\n }\n\n return {host: hostname}\n}\n"],"names":["getApplication","getApplicationUrl","listApplications","getUserApplication","getUserApplications","APP_ID_NOT_FOUND_IN_ORGANIZATION","normalizeUrl","validateUrl","resolveStudioDeployTarget","options","appId","isExternal","projectId","studioHost","urlFlag","message","type","application","isSdkApp","reason","error","hostError","host","resolvedHost","resolveAppHost","url","appHost","validation","existing","listStudioApplications","resolveAppDeployTarget","organizationId","appType","length","resolveWorkbenchApp","slug","resolveAppById","find","app","resolveWorkbenchStudio","resolution","urlType","applications","filter","normalized","hostname","replace","includes","test"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { getSanityEnvVar } from '@sanity/cli-core';
3
3
  import { logSymbols, spinner } from '@sanity/cli-core/ux';
4
- import { isWorkbenchApp, resolveAppId } from '@sanity/workbench-cli';
4
+ import { isWorkbenchApp } from '@sanity/workbench-cli';
5
5
  import { determineIsApp } from '../../../util/determineIsApp.js';
6
6
  import { getSharedServerConfig } from '../../../util/getSharedServerConfig.js';
7
7
  import { resolveReactStrictMode } from '../../../util/resolveReactStrictMode.js';
@@ -50,13 +50,7 @@ export function getDevServerConfig({ cliConfig, flags, httpPort, output, workDir
50
50
  reactStrictMode,
51
51
  staticPath: path.join(workDir, 'static'),
52
52
  typegen: cliConfig?.typegen,
53
- // Inline the same id the dev registry advertises so the app's bus identity
54
- // matches how the workbench addresses it. The registry re-reads the bound
55
- // port if a non-strict shift moves it.
56
- workbenchAppId: isWorkbenchApp(app) ? resolveAppId({
57
- host: baseConfig.httpHost,
58
- port: httpPort ?? baseConfig.httpPort
59
- }) : undefined
53
+ workbenchAppId: isWorkbenchApp(app) ? app.slug : undefined
60
54
  };
61
55
  }
62
56
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/dev/servers/getDevServerConfig.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type CliConfig, getSanityEnvVar, type Output} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {isWorkbenchApp, resolveAppId} from '@sanity/workbench-cli'\n\nimport {type DevServerOptions} from '../../../server/devServer.js'\nimport {determineIsApp} from '../../../util/determineIsApp.js'\nimport {getSharedServerConfig} from '../../../util/getSharedServerConfig.js'\nimport {resolveReactStrictMode} from '../../../util/resolveReactStrictMode.js'\nimport {type DevFlags} from '../types.js'\n\nexport function getDevServerConfig({\n cliConfig,\n flags,\n httpPort,\n output,\n workDir,\n}: {\n cliConfig?: CliConfig\n flags: DevFlags\n httpPort?: number\n output: Output\n workDir: string\n}): Omit<DevServerOptions, 'spinner'> {\n const configSpinner = spinner('Checking configuration files...')\n\n const baseConfig = getSharedServerConfig({\n cliConfig,\n flags: {\n host: flags.host,\n port: flags.port,\n },\n workDir,\n })\n\n configSpinner.succeed()\n\n const isApp = cliConfig ? determineIsApp(cliConfig) : false\n const reactStrictMode = resolveReactStrictMode(cliConfig)\n // `views`/`services` are declared via `unstable_defineApp`, so read them off\n // the branded app result rather than the legacy `app` config type.\n const app = cliConfig?.app\n\n const envBasePath = getSanityEnvVar('BASEPATH', isApp ?? false)\n if (envBasePath && cliConfig?.project?.basePath) {\n output.warn(\n `Overriding configured base path (${cliConfig.project.basePath}) with value from environment variable (${envBasePath})`,\n )\n }\n\n // Unstable opt-in to Vite's experimental bundled dev mode, via\n // `unstable_bundledDev` in sanity.cli.ts. Defaults to off.\n const bundledDev = cliConfig?.unstable_bundledDev ?? false\n if (bundledDev) {\n output.log(`${logSymbols.info} Running dev server with experimental Vite bundled dev mode`)\n }\n\n return {\n ...baseConfig,\n bundledDev,\n // The app's navigable entry. A branded app that omits `entry` has no app\n // view: the runtime/federation skip the `./App` render path entirely.\n entry: app?.entry,\n exposes: isWorkbenchApp(app)\n ? {config: app.config, services: app.services, views: app.views}\n : undefined,\n // `devAction` passes an explicit port when a running workbench claimed the\n // configured one; otherwise the shared resolution stands.\n httpPort: httpPort ?? baseConfig.httpPort,\n isWorkbenchApp: isWorkbenchApp(app),\n reactCompiler: cliConfig && 'reactCompiler' in cliConfig ? cliConfig.reactCompiler : undefined,\n reactStrictMode,\n staticPath: path.join(workDir, 'static'),\n typegen: cliConfig?.typegen,\n // Inline the same id the dev registry advertises so the app's bus identity\n // matches how the workbench addresses it. The registry re-reads the bound\n // port if a non-strict shift moves it.\n workbenchAppId: isWorkbenchApp(app)\n ? resolveAppId({host: baseConfig.httpHost, port: httpPort ?? baseConfig.httpPort})\n : undefined,\n }\n}\n"],"names":["path","getSanityEnvVar","logSymbols","spinner","isWorkbenchApp","resolveAppId","determineIsApp","getSharedServerConfig","resolveReactStrictMode","getDevServerConfig","cliConfig","flags","httpPort","output","workDir","configSpinner","baseConfig","host","port","succeed","isApp","reactStrictMode","app","envBasePath","project","basePath","warn","bundledDev","unstable_bundledDev","log","info","entry","exposes","config","services","views","undefined","reactCompiler","staticPath","join","typegen","workbenchAppId","httpHost"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAE5B,SAAwBC,eAAe,QAAoB,mBAAkB;AAC7E,SAAQC,UAAU,EAAEC,OAAO,QAAO,sBAAqB;AACvD,SAAQC,cAAc,EAAEC,YAAY,QAAO,wBAAuB;AAGlE,SAAQC,cAAc,QAAO,kCAAiC;AAC9D,SAAQC,qBAAqB,QAAO,yCAAwC;AAC5E,SAAQC,sBAAsB,QAAO,0CAAyC;AAG9E,OAAO,SAASC,mBAAmB,EACjCC,SAAS,EACTC,KAAK,EACLC,QAAQ,EACRC,MAAM,EACNC,OAAO,EAOR;IACC,MAAMC,gBAAgBZ,QAAQ;IAE9B,MAAMa,aAAaT,sBAAsB;QACvCG;QACAC,OAAO;YACLM,MAAMN,MAAMM,IAAI;YAChBC,MAAMP,MAAMO,IAAI;QAClB;QACAJ;IACF;IAEAC,cAAcI,OAAO;IAErB,MAAMC,QAAQV,YAAYJ,eAAeI,aAAa;IACtD,MAAMW,kBAAkBb,uBAAuBE;IAC/C,6EAA6E;IAC7E,mEAAmE;IACnE,MAAMY,MAAMZ,WAAWY;IAEvB,MAAMC,cAActB,gBAAgB,YAAYmB,SAAS;IACzD,IAAIG,eAAeb,WAAWc,SAASC,UAAU;QAC/CZ,OAAOa,IAAI,CACT,CAAC,iCAAiC,EAAEhB,UAAUc,OAAO,CAACC,QAAQ,CAAC,wCAAwC,EAAEF,YAAY,CAAC,CAAC;IAE3H;IAEA,+DAA+D;IAC/D,2DAA2D;IAC3D,MAAMI,aAAajB,WAAWkB,uBAAuB;IACrD,IAAID,YAAY;QACdd,OAAOgB,GAAG,CAAC,GAAG3B,WAAW4B,IAAI,CAAC,2DAA2D,CAAC;IAC5F;IAEA,OAAO;QACL,GAAGd,UAAU;QACbW;QACA,yEAAyE;QACzE,sEAAsE;QACtEI,OAAOT,KAAKS;QACZC,SAAS5B,eAAekB,OACpB;YAACW,QAAQX,IAAIW,MAAM;YAAEC,UAAUZ,IAAIY,QAAQ;YAAEC,OAAOb,IAAIa,KAAK;QAAA,IAC7DC;QACJ,2EAA2E;QAC3E,0DAA0D;QAC1DxB,UAAUA,YAAYI,WAAWJ,QAAQ;QACzCR,gBAAgBA,eAAekB;QAC/Be,eAAe3B,aAAa,mBAAmBA,YAAYA,UAAU2B,aAAa,GAAGD;QACrFf;QACAiB,YAAYtC,KAAKuC,IAAI,CAACzB,SAAS;QAC/B0B,SAAS9B,WAAW8B;QACpB,2EAA2E;QAC3E,0EAA0E;QAC1E,uCAAuC;QACvCC,gBAAgBrC,eAAekB,OAC3BjB,aAAa;YAACY,MAAMD,WAAW0B,QAAQ;YAAExB,MAAMN,YAAYI,WAAWJ,QAAQ;QAAA,KAC9EwB;IACN;AACF"}
1
+ {"version":3,"sources":["../../../../src/actions/dev/servers/getDevServerConfig.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type CliConfig, getSanityEnvVar, type Output} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {isWorkbenchApp} from '@sanity/workbench-cli'\n\nimport {type DevServerOptions} from '../../../server/devServer.js'\nimport {determineIsApp} from '../../../util/determineIsApp.js'\nimport {getSharedServerConfig} from '../../../util/getSharedServerConfig.js'\nimport {resolveReactStrictMode} from '../../../util/resolveReactStrictMode.js'\nimport {type DevFlags} from '../types.js'\n\nexport function getDevServerConfig({\n cliConfig,\n flags,\n httpPort,\n output,\n workDir,\n}: {\n cliConfig?: CliConfig\n flags: DevFlags\n httpPort?: number\n output: Output\n workDir: string\n}): Omit<DevServerOptions, 'spinner'> {\n const configSpinner = spinner('Checking configuration files...')\n\n const baseConfig = getSharedServerConfig({\n cliConfig,\n flags: {\n host: flags.host,\n port: flags.port,\n },\n workDir,\n })\n\n configSpinner.succeed()\n\n const isApp = cliConfig ? determineIsApp(cliConfig) : false\n const reactStrictMode = resolveReactStrictMode(cliConfig)\n // `views`/`services` are declared via `unstable_defineApp`, so read them off\n // the branded app result rather than the legacy `app` config type.\n const app = cliConfig?.app\n\n const envBasePath = getSanityEnvVar('BASEPATH', isApp ?? false)\n if (envBasePath && cliConfig?.project?.basePath) {\n output.warn(\n `Overriding configured base path (${cliConfig.project.basePath}) with value from environment variable (${envBasePath})`,\n )\n }\n\n // Unstable opt-in to Vite's experimental bundled dev mode, via\n // `unstable_bundledDev` in sanity.cli.ts. Defaults to off.\n const bundledDev = cliConfig?.unstable_bundledDev ?? false\n if (bundledDev) {\n output.log(`${logSymbols.info} Running dev server with experimental Vite bundled dev mode`)\n }\n\n return {\n ...baseConfig,\n bundledDev,\n // The app's navigable entry. A branded app that omits `entry` has no app\n // view: the runtime/federation skip the `./App` render path entirely.\n entry: app?.entry,\n exposes: isWorkbenchApp(app)\n ? {config: app.config, services: app.services, views: app.views}\n : undefined,\n // `devAction` passes an explicit port when a running workbench claimed the\n // configured one; otherwise the shared resolution stands.\n httpPort: httpPort ?? baseConfig.httpPort,\n isWorkbenchApp: isWorkbenchApp(app),\n reactCompiler: cliConfig && 'reactCompiler' in cliConfig ? cliConfig.reactCompiler : undefined,\n reactStrictMode,\n staticPath: path.join(workDir, 'static'),\n typegen: cliConfig?.typegen,\n workbenchAppId: isWorkbenchApp(app) ? app.slug : undefined,\n }\n}\n"],"names":["path","getSanityEnvVar","logSymbols","spinner","isWorkbenchApp","determineIsApp","getSharedServerConfig","resolveReactStrictMode","getDevServerConfig","cliConfig","flags","httpPort","output","workDir","configSpinner","baseConfig","host","port","succeed","isApp","reactStrictMode","app","envBasePath","project","basePath","warn","bundledDev","unstable_bundledDev","log","info","entry","exposes","config","services","views","undefined","reactCompiler","staticPath","join","typegen","workbenchAppId","slug"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAE5B,SAAwBC,eAAe,QAAoB,mBAAkB;AAC7E,SAAQC,UAAU,EAAEC,OAAO,QAAO,sBAAqB;AACvD,SAAQC,cAAc,QAAO,wBAAuB;AAGpD,SAAQC,cAAc,QAAO,kCAAiC;AAC9D,SAAQC,qBAAqB,QAAO,yCAAwC;AAC5E,SAAQC,sBAAsB,QAAO,0CAAyC;AAG9E,OAAO,SAASC,mBAAmB,EACjCC,SAAS,EACTC,KAAK,EACLC,QAAQ,EACRC,MAAM,EACNC,OAAO,EAOR;IACC,MAAMC,gBAAgBX,QAAQ;IAE9B,MAAMY,aAAaT,sBAAsB;QACvCG;QACAC,OAAO;YACLM,MAAMN,MAAMM,IAAI;YAChBC,MAAMP,MAAMO,IAAI;QAClB;QACAJ;IACF;IAEAC,cAAcI,OAAO;IAErB,MAAMC,QAAQV,YAAYJ,eAAeI,aAAa;IACtD,MAAMW,kBAAkBb,uBAAuBE;IAC/C,6EAA6E;IAC7E,mEAAmE;IACnE,MAAMY,MAAMZ,WAAWY;IAEvB,MAAMC,cAAcrB,gBAAgB,YAAYkB,SAAS;IACzD,IAAIG,eAAeb,WAAWc,SAASC,UAAU;QAC/CZ,OAAOa,IAAI,CACT,CAAC,iCAAiC,EAAEhB,UAAUc,OAAO,CAACC,QAAQ,CAAC,wCAAwC,EAAEF,YAAY,CAAC,CAAC;IAE3H;IAEA,+DAA+D;IAC/D,2DAA2D;IAC3D,MAAMI,aAAajB,WAAWkB,uBAAuB;IACrD,IAAID,YAAY;QACdd,OAAOgB,GAAG,CAAC,GAAG1B,WAAW2B,IAAI,CAAC,2DAA2D,CAAC;IAC5F;IAEA,OAAO;QACL,GAAGd,UAAU;QACbW;QACA,yEAAyE;QACzE,sEAAsE;QACtEI,OAAOT,KAAKS;QACZC,SAAS3B,eAAeiB,OACpB;YAACW,QAAQX,IAAIW,MAAM;YAAEC,UAAUZ,IAAIY,QAAQ;YAAEC,OAAOb,IAAIa,KAAK;QAAA,IAC7DC;QACJ,2EAA2E;QAC3E,0DAA0D;QAC1DxB,UAAUA,YAAYI,WAAWJ,QAAQ;QACzCP,gBAAgBA,eAAeiB;QAC/Be,eAAe3B,aAAa,mBAAmBA,YAAYA,UAAU2B,aAAa,GAAGD;QACrFf;QACAiB,YAAYrC,KAAKsC,IAAI,CAACzB,SAAS;QAC/B0B,SAAS9B,WAAW8B;QACpBC,gBAAgBpC,eAAeiB,OAAOA,IAAIoB,IAAI,GAAGN;IACnD;AACF"}
@@ -10,6 +10,7 @@ import { getProjectById } from '../../../services/projects.js';
10
10
  import { getAppId } from '../../../util/appId.js';
11
11
  import { getPackageManagerChoice } from '../../../util/packageManager/packageManagerChoice.js';
12
12
  import { upgradePackages } from '../../../util/packageManager/upgradePackages.js';
13
+ import { hasLocalUnclaimedProject } from '../../../util/unclaimedProjects.js';
13
14
  import { checkDependenciesEventListenerFactory } from '../../build/eventListenerFactory.js';
14
15
  import { shouldAutoUpdate } from '../../build/shouldAutoUpdate.js';
15
16
  import { devDebug } from '../devDebug.js';
@@ -141,7 +142,7 @@ export async function startStudioDevServer(options) {
141
142
  })));
142
143
  } else {
143
144
  const startupDuration = Date.now() - startTime;
144
- const url = `http://${httpHost || 'localhost'}:${port}${config.basePath}`;
145
+ const url = withUnclaimedSignInHash(`http://${httpHost || 'localhost'}:${port}${config.basePath}`, projectId);
145
146
  const appType = 'Sanity Studio';
146
147
  const viteVersion = await getLocalPackageVersion('vite', import.meta.url);
147
148
  spin.succeed();
@@ -157,5 +158,10 @@ export async function startStudioDevServer(options) {
157
158
  throw gracefulServerDeath('dev', config.httpHost, config.httpPort, err);
158
159
  }
159
160
  }
161
+ function withUnclaimedSignInHash(url, projectId) {
162
+ const token = process.env.SANITY_AUTH_TOKEN;
163
+ if (!token || !projectId || !hasLocalUnclaimedProject(projectId)) return url;
164
+ return `${url}#token=${encodeURIComponent(token)}`;
165
+ }
160
166
 
161
167
  //# sourceMappingURL=startStudioDevServer.js.map