@sanity/cli 7.7.0 → 7.8.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 (74) hide show
  1. package/README.md +17 -2
  2. package/dist/actions/auth/ensureAuthenticated.js +1 -1
  3. package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
  4. package/dist/actions/auth/login/validateToken.js +1 -1
  5. package/dist/actions/auth/login/validateToken.js.map +1 -1
  6. package/dist/actions/build/buildApp.js +2 -3
  7. package/dist/actions/build/buildApp.js.map +1 -1
  8. package/dist/actions/build/buildStudio.js +1 -2
  9. package/dist/actions/build/buildStudio.js.map +1 -1
  10. package/dist/actions/deploy/createUserApplication.js +8 -8
  11. package/dist/actions/deploy/createUserApplication.js.map +1 -1
  12. package/dist/actions/deploy/deployApp.js +121 -24
  13. package/dist/actions/deploy/deployApp.js.map +1 -1
  14. package/dist/actions/deploy/deployChecks.js +99 -68
  15. package/dist/actions/deploy/deployChecks.js.map +1 -1
  16. package/dist/actions/deploy/deployRunner.js +43 -24
  17. package/dist/actions/deploy/deployRunner.js.map +1 -1
  18. package/dist/actions/deploy/deployStudio.js +126 -23
  19. package/dist/actions/deploy/deployStudio.js.map +1 -1
  20. package/dist/actions/deploy/deploymentPlan.js +29 -30
  21. package/dist/actions/deploy/deploymentPlan.js.map +1 -1
  22. package/dist/actions/deploy/findUserApplication.js +39 -14
  23. package/dist/actions/deploy/findUserApplication.js.map +1 -1
  24. package/dist/actions/deploy/resolveDeployTarget.js +43 -0
  25. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  26. package/dist/actions/dev/servers/getDevServerConfig.js +1 -1
  27. package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
  28. package/dist/actions/dev/servers/startStudioDevServer.js +1 -2
  29. package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
  30. package/dist/actions/graphql/SchemaError.js.map +1 -1
  31. package/dist/actions/graphql/extractGraphQLAPIs.js +3 -1
  32. package/dist/actions/graphql/extractGraphQLAPIs.js.map +1 -1
  33. package/dist/actions/manifest/extractCoreAppManifest.js +1 -1
  34. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  35. package/dist/actions/organizations/getOrganization.js +1 -1
  36. package/dist/actions/organizations/getOrganization.js.map +1 -1
  37. package/dist/actions/schema/validateAction.js +1 -1
  38. package/dist/actions/schema/validateAction.js.map +1 -1
  39. package/dist/actions/skills/setupSkills.js +1 -1
  40. package/dist/actions/skills/setupSkills.js.map +1 -1
  41. package/dist/actions/undeploy/adapters.js +101 -0
  42. package/dist/actions/undeploy/adapters.js.map +1 -0
  43. package/dist/actions/undeploy/runUndeploy.js +165 -0
  44. package/dist/actions/undeploy/runUndeploy.js.map +1 -0
  45. package/dist/actions/undeploy/undeployPlan.js +108 -0
  46. package/dist/actions/undeploy/undeployPlan.js.map +1 -0
  47. package/dist/commands/datasets/copy.js +2 -1
  48. package/dist/commands/datasets/copy.js.map +1 -1
  49. package/dist/commands/mcp/configure.js +1 -1
  50. package/dist/commands/mcp/configure.js.map +1 -1
  51. package/dist/commands/projects/create.js +18 -13
  52. package/dist/commands/projects/create.js.map +1 -1
  53. package/dist/commands/projects/list.js +2 -1
  54. package/dist/commands/projects/list.js.map +1 -1
  55. package/dist/commands/schemas/validate.js +3 -2
  56. package/dist/commands/schemas/validate.js.map +1 -1
  57. package/dist/commands/skills/install.js +1 -1
  58. package/dist/commands/skills/install.js.map +1 -1
  59. package/dist/commands/tokens/list.js +1 -1
  60. package/dist/commands/tokens/list.js.map +1 -1
  61. package/dist/commands/undeploy.js +36 -74
  62. package/dist/commands/undeploy.js.map +1 -1
  63. package/dist/util/checks.js +83 -0
  64. package/dist/util/checks.js.map +1 -0
  65. package/dist/util/toStderrOutput.js +13 -0
  66. package/dist/util/toStderrOutput.js.map +1 -0
  67. package/oclif.manifest.json +1298 -1271
  68. package/package.json +6 -7
  69. package/dist/actions/undeploy/getStudioOrAppUserApplication.js +0 -36
  70. package/dist/actions/undeploy/getStudioOrAppUserApplication.js.map +0 -1
  71. package/dist/util/compareDependencyVersions.js +0 -110
  72. package/dist/util/compareDependencyVersions.js.map +0 -1
  73. package/dist/util/getErrorMessage.js +0 -28
  74. package/dist/util/getErrorMessage.js.map +0 -1
package/README.md CHANGED
@@ -3427,13 +3427,28 @@ Removes the deployed Sanity Studio/App from Sanity hosting
3427
3427
 
3428
3428
  ```
3429
3429
  USAGE
3430
- $ sanity undeploy [-y]
3430
+ $ sanity undeploy [--dry-run] [-j] [-y]
3431
3431
 
3432
3432
  FLAGS
3433
- -y, --yes Unattended mode, answers "yes" to any "yes/no" prompt and otherwise uses defaults
3433
+ -j, --json Output the result as JSON
3434
+ -y, --yes Unattended mode, answers "yes" to any "yes/no" prompt and otherwise uses defaults
3435
+ --dry-run Report what would be undeployed without deleting anything
3434
3436
 
3435
3437
  DESCRIPTION
3436
3438
  Removes the deployed Sanity Studio/App from Sanity hosting
3439
+
3440
+ EXAMPLES
3441
+ Undeploy the studio or application after confirming
3442
+
3443
+ $ sanity undeploy
3444
+
3445
+ Report what would be undeployed without deleting anything
3446
+
3447
+ $ sanity undeploy --dry-run
3448
+
3449
+ Undeploy without prompting and report the result as JSON
3450
+
3451
+ $ sanity undeploy --json --yes
3437
3452
  ```
3438
3453
 
3439
3454
  ## `sanity users invite [EMAIL]`
@@ -1,8 +1,8 @@
1
1
  import { getCliToken } from '@sanity/cli-core';
2
+ import { getErrorMessage } from '@sanity/cli-core/errors';
2
3
  import { isHttpError } from '@sanity/client';
3
4
  import { LoginError } from '../../errors/LoginError.js';
4
5
  import { getCliUser } from '../../services/user.js';
5
- import { getErrorMessage } from '../../util/getErrorMessage.js';
6
6
  import { login } from './login/login.js';
7
7
  /**
8
8
  * Ensure the user is authenticated. Validates the current token via /users/me
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/auth/ensureAuthenticated.ts"],"sourcesContent":["import {\n type CLITelemetryStore,\n getCliToken,\n type Output,\n type SanityOrgUser,\n} from '@sanity/cli-core'\nimport {isHttpError} from '@sanity/client'\n\nimport {LoginError} from '../../errors/LoginError.js'\nimport {getCliUser} from '../../services/user.js'\nimport {getErrorMessage} from '../../util/getErrorMessage.js'\nimport {login} from './login/login.js'\n\n/**\n * Ensure the user is authenticated. Validates the current token via /users/me\n * and triggers interactive login if the token is missing or invalid.\n *\n * @returns The authenticated user.\n * @throws LoginError if interactive login fails or is cancelled.\n * @throws Error if a network/server error occurs during validation.\n * @internal\n */\nexport async function ensureAuthenticated(options: {\n output: Output\n telemetry: CLITelemetryStore\n}): Promise<SanityOrgUser> {\n const user = await validateSession()\n if (user) return user\n\n try {\n await login({output: options.output, telemetry: options.telemetry})\n } catch (cause) {\n throw new LoginError(getErrorMessage(cause), {cause})\n }\n\n try {\n return await getCliUser()\n } catch (cause) {\n if (isHttpError(cause) && (cause.statusCode === 401 || cause.statusCode === 403)) {\n throw new LoginError(\n `Login succeeded but failed to verify session: ${getErrorMessage(cause)}`,\n {cause},\n )\n }\n throw cause\n }\n}\n\n/**\n * Validate the current CLI auth token by calling /users/me.\n *\n * Returns null only for auth failures (401/403). Re-throws network errors,\n * timeouts, and server errors so callers can handle them appropriately\n * instead of triggering an unnecessary re-login prompt.\n *\n * @returns The authenticated user if valid, null if invalid/missing.\n * @internal\n */\nexport async function validateSession(): Promise<SanityOrgUser | null> {\n const token = await getCliToken()\n if (!token) return null\n\n try {\n return await getCliUser()\n } catch (error) {\n if (isHttpError(error) && (error.statusCode === 401 || error.statusCode === 403)) {\n return null\n }\n throw error\n }\n}\n"],"names":["getCliToken","isHttpError","LoginError","getCliUser","getErrorMessage","login","ensureAuthenticated","options","user","validateSession","output","telemetry","cause","statusCode","token","error"],"mappings":"AAAA,SAEEA,WAAW,QAGN,mBAAkB;AACzB,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,UAAU,QAAO,6BAA4B;AACrD,SAAQC,UAAU,QAAO,yBAAwB;AACjD,SAAQC,eAAe,QAAO,gCAA+B;AAC7D,SAAQC,KAAK,QAAO,mBAAkB;AAEtC;;;;;;;;CAQC,GACD,OAAO,eAAeC,oBAAoBC,OAGzC;IACC,MAAMC,OAAO,MAAMC;IACnB,IAAID,MAAM,OAAOA;IAEjB,IAAI;QACF,MAAMH,MAAM;YAACK,QAAQH,QAAQG,MAAM;YAAEC,WAAWJ,QAAQI,SAAS;QAAA;IACnE,EAAE,OAAOC,OAAO;QACd,MAAM,IAAIV,WAAWE,gBAAgBQ,QAAQ;YAACA;QAAK;IACrD;IAEA,IAAI;QACF,OAAO,MAAMT;IACf,EAAE,OAAOS,OAAO;QACd,IAAIX,YAAYW,UAAWA,CAAAA,MAAMC,UAAU,KAAK,OAAOD,MAAMC,UAAU,KAAK,GAAE,GAAI;YAChF,MAAM,IAAIX,WACR,CAAC,8CAA8C,EAAEE,gBAAgBQ,QAAQ,EACzE;gBAACA;YAAK;QAEV;QACA,MAAMA;IACR;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,eAAeH;IACpB,MAAMK,QAAQ,MAAMd;IACpB,IAAI,CAACc,OAAO,OAAO;IAEnB,IAAI;QACF,OAAO,MAAMX;IACf,EAAE,OAAOY,OAAO;QACd,IAAId,YAAYc,UAAWA,CAAAA,MAAMF,UAAU,KAAK,OAAOE,MAAMF,UAAU,KAAK,GAAE,GAAI;YAChF,OAAO;QACT;QACA,MAAME;IACR;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/auth/ensureAuthenticated.ts"],"sourcesContent":["import {\n type CLITelemetryStore,\n getCliToken,\n type Output,\n type SanityOrgUser,\n} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {LoginError} from '../../errors/LoginError.js'\nimport {getCliUser} from '../../services/user.js'\nimport {login} from './login/login.js'\n\n/**\n * Ensure the user is authenticated. Validates the current token via /users/me\n * and triggers interactive login if the token is missing or invalid.\n *\n * @returns The authenticated user.\n * @throws LoginError if interactive login fails or is cancelled.\n * @throws Error if a network/server error occurs during validation.\n * @internal\n */\nexport async function ensureAuthenticated(options: {\n output: Output\n telemetry: CLITelemetryStore\n}): Promise<SanityOrgUser> {\n const user = await validateSession()\n if (user) return user\n\n try {\n await login({output: options.output, telemetry: options.telemetry})\n } catch (cause) {\n throw new LoginError(getErrorMessage(cause), {cause})\n }\n\n try {\n return await getCliUser()\n } catch (cause) {\n if (isHttpError(cause) && (cause.statusCode === 401 || cause.statusCode === 403)) {\n throw new LoginError(\n `Login succeeded but failed to verify session: ${getErrorMessage(cause)}`,\n {cause},\n )\n }\n throw cause\n }\n}\n\n/**\n * Validate the current CLI auth token by calling /users/me.\n *\n * Returns null only for auth failures (401/403). Re-throws network errors,\n * timeouts, and server errors so callers can handle them appropriately\n * instead of triggering an unnecessary re-login prompt.\n *\n * @returns The authenticated user if valid, null if invalid/missing.\n * @internal\n */\nexport async function validateSession(): Promise<SanityOrgUser | null> {\n const token = await getCliToken()\n if (!token) return null\n\n try {\n return await getCliUser()\n } catch (error) {\n if (isHttpError(error) && (error.statusCode === 401 || error.statusCode === 403)) {\n return null\n }\n throw error\n }\n}\n"],"names":["getCliToken","getErrorMessage","isHttpError","LoginError","getCliUser","login","ensureAuthenticated","options","user","validateSession","output","telemetry","cause","statusCode","token","error"],"mappings":"AAAA,SAEEA,WAAW,QAGN,mBAAkB;AACzB,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,UAAU,QAAO,6BAA4B;AACrD,SAAQC,UAAU,QAAO,yBAAwB;AACjD,SAAQC,KAAK,QAAO,mBAAkB;AAEtC;;;;;;;;CAQC,GACD,OAAO,eAAeC,oBAAoBC,OAGzC;IACC,MAAMC,OAAO,MAAMC;IACnB,IAAID,MAAM,OAAOA;IAEjB,IAAI;QACF,MAAMH,MAAM;YAACK,QAAQH,QAAQG,MAAM;YAAEC,WAAWJ,QAAQI,SAAS;QAAA;IACnE,EAAE,OAAOC,OAAO;QACd,MAAM,IAAIT,WAAWF,gBAAgBW,QAAQ;YAACA;QAAK;IACrD;IAEA,IAAI;QACF,OAAO,MAAMR;IACf,EAAE,OAAOQ,OAAO;QACd,IAAIV,YAAYU,UAAWA,CAAAA,MAAMC,UAAU,KAAK,OAAOD,MAAMC,UAAU,KAAK,GAAE,GAAI;YAChF,MAAM,IAAIV,WACR,CAAC,8CAA8C,EAAEF,gBAAgBW,QAAQ,EACzE;gBAACA;YAAK;QAEV;QACA,MAAMA;IACR;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,eAAeH;IACpB,MAAMK,QAAQ,MAAMd;IACpB,IAAI,CAACc,OAAO,OAAO;IAEnB,IAAI;QACF,OAAO,MAAMV;IACf,EAAE,OAAOW,OAAO;QACd,IAAIb,YAAYa,UAAWA,CAAAA,MAAMF,UAAU,KAAK,OAAOE,MAAMF,UAAU,KAAK,GAAE,GAAI;YAChF,OAAO;QACT;QACA,MAAME;IACR;AACF"}
@@ -1,7 +1,7 @@
1
1
  import { getGlobalCliClient } from '@sanity/cli-core';
2
+ import { getErrorMessage } from '@sanity/cli-core/errors';
2
3
  import { isHttpError } from '@sanity/client';
3
4
  import { USERS_API_VERSION } from '../../../services/user.js';
4
- import { getErrorMessage } from '../../../util/getErrorMessage.js';
5
5
  export async function validateToken(token) {
6
6
  const trimmedToken = token.trim();
7
7
  if (!trimmedToken) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/auth/login/validateToken.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core'\nimport {isHttpError} from '@sanity/client'\n\nimport {USERS_API_VERSION} from '../../../services/user.js'\nimport {getErrorMessage} from '../../../util/getErrorMessage.js'\n\nexport async function validateToken(token: string): Promise<string> {\n const trimmedToken = token.trim()\n\n if (!trimmedToken) {\n throw new Error(\n 'Token is required on standard input. Run `sanity login --with-token < token.txt`.',\n )\n }\n\n try {\n await getTokenUser(trimmedToken)\n } catch (error) {\n if (isHttpError(error) && (error.statusCode === 401 || error.statusCode === 403)) {\n throw new Error('Token is invalid or expired. Check the token and try again.', {cause: error})\n }\n\n throw new Error(`Could not verify token: ${getErrorMessage(error)}`, {cause: error})\n }\n\n return trimmedToken\n}\n\nexport async function isSanityApiToken(token: string): Promise<boolean> {\n return isSanityApiTokenUser(await getTokenUser(token))\n}\n\nasync function getTokenUser(token: string): Promise<unknown> {\n const client = await getGlobalCliClient({\n apiVersion: USERS_API_VERSION,\n requireUser: true,\n token,\n })\n\n return client.users.getById('me')\n}\n\nfunction isSanityApiTokenUser(user: unknown): boolean {\n return (\n typeof user === 'object' &&\n user !== null &&\n 'provider' in user &&\n (user as {provider?: unknown}).provider === 'sanity-token'\n )\n}\n"],"names":["getGlobalCliClient","isHttpError","USERS_API_VERSION","getErrorMessage","validateToken","token","trimmedToken","trim","Error","getTokenUser","error","statusCode","cause","isSanityApiToken","isSanityApiTokenUser","client","apiVersion","requireUser","users","getById","user","provider"],"mappings":"AAAA,SAAQA,kBAAkB,QAAO,mBAAkB;AACnD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,iBAAiB,QAAO,4BAA2B;AAC3D,SAAQC,eAAe,QAAO,mCAAkC;AAEhE,OAAO,eAAeC,cAAcC,KAAa;IAC/C,MAAMC,eAAeD,MAAME,IAAI;IAE/B,IAAI,CAACD,cAAc;QACjB,MAAM,IAAIE,MACR;IAEJ;IAEA,IAAI;QACF,MAAMC,aAAaH;IACrB,EAAE,OAAOI,OAAO;QACd,IAAIT,YAAYS,UAAWA,CAAAA,MAAMC,UAAU,KAAK,OAAOD,MAAMC,UAAU,KAAK,GAAE,GAAI;YAChF,MAAM,IAAIH,MAAM,+DAA+D;gBAACI,OAAOF;YAAK;QAC9F;QAEA,MAAM,IAAIF,MAAM,CAAC,wBAAwB,EAAEL,gBAAgBO,QAAQ,EAAE;YAACE,OAAOF;QAAK;IACpF;IAEA,OAAOJ;AACT;AAEA,OAAO,eAAeO,iBAAiBR,KAAa;IAClD,OAAOS,qBAAqB,MAAML,aAAaJ;AACjD;AAEA,eAAeI,aAAaJ,KAAa;IACvC,MAAMU,SAAS,MAAMf,mBAAmB;QACtCgB,YAAYd;QACZe,aAAa;QACbZ;IACF;IAEA,OAAOU,OAAOG,KAAK,CAACC,OAAO,CAAC;AAC9B;AAEA,SAASL,qBAAqBM,IAAa;IACzC,OACE,OAAOA,SAAS,YAChBA,SAAS,QACT,cAAcA,QACd,AAACA,KAA8BC,QAAQ,KAAK;AAEhD"}
1
+ {"version":3,"sources":["../../../../src/actions/auth/login/validateToken.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {USERS_API_VERSION} from '../../../services/user.js'\n\nexport async function validateToken(token: string): Promise<string> {\n const trimmedToken = token.trim()\n\n if (!trimmedToken) {\n throw new Error(\n 'Token is required on standard input. Run `sanity login --with-token < token.txt`.',\n )\n }\n\n try {\n await getTokenUser(trimmedToken)\n } catch (error) {\n if (isHttpError(error) && (error.statusCode === 401 || error.statusCode === 403)) {\n throw new Error('Token is invalid or expired. Check the token and try again.', {cause: error})\n }\n\n throw new Error(`Could not verify token: ${getErrorMessage(error)}`, {cause: error})\n }\n\n return trimmedToken\n}\n\nexport async function isSanityApiToken(token: string): Promise<boolean> {\n return isSanityApiTokenUser(await getTokenUser(token))\n}\n\nasync function getTokenUser(token: string): Promise<unknown> {\n const client = await getGlobalCliClient({\n apiVersion: USERS_API_VERSION,\n requireUser: true,\n token,\n })\n\n return client.users.getById('me')\n}\n\nfunction isSanityApiTokenUser(user: unknown): boolean {\n return (\n typeof user === 'object' &&\n user !== null &&\n 'provider' in user &&\n (user as {provider?: unknown}).provider === 'sanity-token'\n )\n}\n"],"names":["getGlobalCliClient","getErrorMessage","isHttpError","USERS_API_VERSION","validateToken","token","trimmedToken","trim","Error","getTokenUser","error","statusCode","cause","isSanityApiToken","isSanityApiTokenUser","client","apiVersion","requireUser","users","getById","user","provider"],"mappings":"AAAA,SAAQA,kBAAkB,QAAO,mBAAkB;AACnD,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,iBAAiB,QAAO,4BAA2B;AAE3D,OAAO,eAAeC,cAAcC,KAAa;IAC/C,MAAMC,eAAeD,MAAME,IAAI;IAE/B,IAAI,CAACD,cAAc;QACjB,MAAM,IAAIE,MACR;IAEJ;IAEA,IAAI;QACF,MAAMC,aAAaH;IACrB,EAAE,OAAOI,OAAO;QACd,IAAIR,YAAYQ,UAAWA,CAAAA,MAAMC,UAAU,KAAK,OAAOD,MAAMC,UAAU,KAAK,GAAE,GAAI;YAChF,MAAM,IAAIH,MAAM,+DAA+D;gBAACI,OAAOF;YAAK;QAC9F;QAEA,MAAM,IAAIF,MAAM,CAAC,wBAAwB,EAAEP,gBAAgBS,QAAQ,EAAE;YAACE,OAAOF;QAAK;IACpF;IAEA,OAAOJ;AACT;AAEA,OAAO,eAAeO,iBAAiBR,KAAa;IAClD,OAAOS,qBAAqB,MAAML,aAAaJ;AACjD;AAEA,eAAeI,aAAaJ,KAAa;IACvC,MAAMU,SAAS,MAAMf,mBAAmB;QACtCgB,YAAYb;QACZc,aAAa;QACbZ;IACF;IAEA,OAAOU,OAAOG,KAAK,CAACC,OAAO,CAAC;AAC9B;AAEA,SAASL,qBAAqBM,IAAa;IACzC,OACE,OAAOA,SAAS,YAChBA,SAAS,QACT,cAAcA,QACd,AAACA,KAA8BC,QAAQ,KAAK;AAEhD"}
@@ -1,7 +1,6 @@
1
- import { buildApp as internalBuildApp } from '@sanity/cli-build/_internal/build';
1
+ import { compareDependencyVersions, buildApp as internalBuildApp } from '@sanity/cli-build/_internal/build';
2
2
  import { resolveWorkbenchApp } from '@sanity/workbench-cli/build';
3
3
  import { getAppId } from '../../util/appId.js';
4
- import { compareDependencyVersions } from '../../util/compareDependencyVersions.js';
5
4
  import { warnAboutMissingAppId } from '../../util/warnAboutMissingAppId.js';
6
5
  import { determineBasePath } from './determineBasePath.js';
7
6
  /**
@@ -36,7 +35,7 @@ import { determineBasePath } from './determineBasePath.js';
36
35
  determineBasePath: ()=>determineBasePath(cliConfig, 'app', output),
37
36
  entry: app?.entry,
38
37
  exposes: workbench ? {
39
- installationConfig: workbench.installationConfig,
38
+ config: workbench.config,
40
39
  services: workbench.services,
41
40
  views: workbench.views
42
41
  } : undefined,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/build/buildApp.ts"],"sourcesContent":["import {buildApp as internalBuildApp} from '@sanity/cli-build/_internal/build'\nimport {resolveWorkbenchApp} from '@sanity/workbench-cli/build'\n\nimport {getAppId} from '../../util/appId.js'\nimport {compareDependencyVersions} from '../../util/compareDependencyVersions.js'\nimport {warnAboutMissingAppId} from '../../util/warnAboutMissingAppId.js'\nimport {determineBasePath} from './determineBasePath.js'\nimport {type BuildOptions} from './types.js'\n\n/**\n * Build the Sanity app.\n *\n * @internal\n */\nexport async function buildApp(options: BuildOptions): Promise<void> {\n const {cliConfig, flags, outDir, output, workDir} = options\n\n const app = cliConfig && 'app' in cliConfig ? cliConfig.app : undefined\n // `views`/`services` live on the branded `unstable_defineApp` result, not the\n // legacy `app` config object — resolve the workbench capability to read them.\n const workbench = resolveWorkbenchApp(cliConfig)\n\n const appId = getAppId(cliConfig)\n\n await internalBuildApp({\n appId,\n appTitle: app?.title,\n autoUpdatesEnabled: options.autoUpdatesEnabled,\n checkAppId: () => {\n // Warn if auto updates enabled but no appId configured.\n // Skip when called from deploy, since deploy handles appId itself\n // (prompts the user and tells them to add it to config).\n if (!appId && !options.calledFromDeploy) {\n warnAboutMissingAppId({appType: 'app', output})\n }\n },\n compareDependencyVersions: (packages) => compareDependencyVersions(packages, workDir, {appId}),\n determineBasePath: () => determineBasePath(cliConfig, 'app', output),\n entry: app?.entry,\n exposes: workbench\n ? {\n installationConfig: workbench.installationConfig,\n services: workbench.services,\n views: workbench.views,\n }\n : undefined,\n isWorkbenchApp: !!workbench,\n minify: flags.minify,\n outDir,\n output,\n reactCompiler: cliConfig && 'reactCompiler' in cliConfig ? cliConfig.reactCompiler : undefined,\n schemaExtraction: cliConfig?.schemaExtraction,\n sourceMap: Boolean(flags['source-maps']),\n stats: flags.stats,\n unattendedMode: flags.yes,\n vite: cliConfig.vite,\n workbenchAppId: workbench?.name,\n workDir,\n })\n}\n"],"names":["buildApp","internalBuildApp","resolveWorkbenchApp","getAppId","compareDependencyVersions","warnAboutMissingAppId","determineBasePath","options","cliConfig","flags","outDir","output","workDir","app","undefined","workbench","appId","appTitle","title","autoUpdatesEnabled","checkAppId","calledFromDeploy","appType","packages","entry","exposes","installationConfig","services","views","isWorkbenchApp","minify","reactCompiler","schemaExtraction","sourceMap","Boolean","stats","unattendedMode","yes","vite","workbenchAppId","name"],"mappings":"AAAA,SAAQA,YAAYC,gBAAgB,QAAO,oCAAmC;AAC9E,SAAQC,mBAAmB,QAAO,8BAA6B;AAE/D,SAAQC,QAAQ,QAAO,sBAAqB;AAC5C,SAAQC,yBAAyB,QAAO,0CAAyC;AACjF,SAAQC,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,iBAAiB,QAAO,yBAAwB;AAGxD;;;;CAIC,GACD,OAAO,eAAeN,SAASO,OAAqB;IAClD,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGL;IAEpD,MAAMM,MAAML,aAAa,SAASA,YAAYA,UAAUK,GAAG,GAAGC;IAC9D,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAMC,YAAYb,oBAAoBM;IAEtC,MAAMQ,QAAQb,SAASK;IAEvB,MAAMP,iBAAiB;QACrBe;QACAC,UAAUJ,KAAKK;QACfC,oBAAoBZ,QAAQY,kBAAkB;QAC9CC,YAAY;YACV,wDAAwD;YACxD,kEAAkE;YAClE,yDAAyD;YACzD,IAAI,CAACJ,SAAS,CAACT,QAAQc,gBAAgB,EAAE;gBACvChB,sBAAsB;oBAACiB,SAAS;oBAAOX;gBAAM;YAC/C;QACF;QACAP,2BAA2B,CAACmB,WAAanB,0BAA0BmB,UAAUX,SAAS;gBAACI;YAAK;QAC5FV,mBAAmB,IAAMA,kBAAkBE,WAAW,OAAOG;QAC7Da,OAAOX,KAAKW;QACZC,SAASV,YACL;YACEW,oBAAoBX,UAAUW,kBAAkB;YAChDC,UAAUZ,UAAUY,QAAQ;YAC5BC,OAAOb,UAAUa,KAAK;QACxB,IACAd;QACJe,gBAAgB,CAAC,CAACd;QAClBe,QAAQrB,MAAMqB,MAAM;QACpBpB;QACAC;QACAoB,eAAevB,aAAa,mBAAmBA,YAAYA,UAAUuB,aAAa,GAAGjB;QACrFkB,kBAAkBxB,WAAWwB;QAC7BC,WAAWC,QAAQzB,KAAK,CAAC,cAAc;QACvC0B,OAAO1B,MAAM0B,KAAK;QAClBC,gBAAgB3B,MAAM4B,GAAG;QACzBC,MAAM9B,UAAU8B,IAAI;QACpBC,gBAAgBxB,WAAWyB;QAC3B5B;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/build/buildApp.ts"],"sourcesContent":["import {\n compareDependencyVersions,\n buildApp as internalBuildApp,\n} from '@sanity/cli-build/_internal/build'\nimport {resolveWorkbenchApp} from '@sanity/workbench-cli/build'\n\nimport {getAppId} from '../../util/appId.js'\nimport {warnAboutMissingAppId} from '../../util/warnAboutMissingAppId.js'\nimport {determineBasePath} from './determineBasePath.js'\nimport {type BuildOptions} from './types.js'\n\n/**\n * Build the Sanity app.\n *\n * @internal\n */\nexport async function buildApp(options: BuildOptions): Promise<void> {\n const {cliConfig, flags, outDir, output, workDir} = options\n\n const app = cliConfig && 'app' in cliConfig ? cliConfig.app : undefined\n // `views`/`services` live on the branded `unstable_defineApp` result, not the\n // legacy `app` config object — resolve the workbench capability to read them.\n const workbench = resolveWorkbenchApp(cliConfig)\n\n const appId = getAppId(cliConfig)\n\n await internalBuildApp({\n appId,\n appTitle: app?.title,\n autoUpdatesEnabled: options.autoUpdatesEnabled,\n checkAppId: () => {\n // Warn if auto updates enabled but no appId configured.\n // Skip when called from deploy, since deploy handles appId itself\n // (prompts the user and tells them to add it to config).\n if (!appId && !options.calledFromDeploy) {\n warnAboutMissingAppId({appType: 'app', output})\n }\n },\n compareDependencyVersions: (packages) => compareDependencyVersions(packages, workDir, {appId}),\n determineBasePath: () => determineBasePath(cliConfig, 'app', output),\n entry: app?.entry,\n exposes: workbench\n ? {\n config: workbench.config,\n services: workbench.services,\n views: workbench.views,\n }\n : undefined,\n isWorkbenchApp: !!workbench,\n minify: flags.minify,\n outDir,\n output,\n reactCompiler: cliConfig && 'reactCompiler' in cliConfig ? cliConfig.reactCompiler : undefined,\n schemaExtraction: cliConfig?.schemaExtraction,\n sourceMap: Boolean(flags['source-maps']),\n stats: flags.stats,\n unattendedMode: flags.yes,\n vite: cliConfig.vite,\n workbenchAppId: workbench?.name,\n workDir,\n })\n}\n"],"names":["compareDependencyVersions","buildApp","internalBuildApp","resolveWorkbenchApp","getAppId","warnAboutMissingAppId","determineBasePath","options","cliConfig","flags","outDir","output","workDir","app","undefined","workbench","appId","appTitle","title","autoUpdatesEnabled","checkAppId","calledFromDeploy","appType","packages","entry","exposes","config","services","views","isWorkbenchApp","minify","reactCompiler","schemaExtraction","sourceMap","Boolean","stats","unattendedMode","yes","vite","workbenchAppId","name"],"mappings":"AAAA,SACEA,yBAAyB,EACzBC,YAAYC,gBAAgB,QACvB,oCAAmC;AAC1C,SAAQC,mBAAmB,QAAO,8BAA6B;AAE/D,SAAQC,QAAQ,QAAO,sBAAqB;AAC5C,SAAQC,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,iBAAiB,QAAO,yBAAwB;AAGxD;;;;CAIC,GACD,OAAO,eAAeL,SAASM,OAAqB;IAClD,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGL;IAEpD,MAAMM,MAAML,aAAa,SAASA,YAAYA,UAAUK,GAAG,GAAGC;IAC9D,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAMC,YAAYZ,oBAAoBK;IAEtC,MAAMQ,QAAQZ,SAASI;IAEvB,MAAMN,iBAAiB;QACrBc;QACAC,UAAUJ,KAAKK;QACfC,oBAAoBZ,QAAQY,kBAAkB;QAC9CC,YAAY;YACV,wDAAwD;YACxD,kEAAkE;YAClE,yDAAyD;YACzD,IAAI,CAACJ,SAAS,CAACT,QAAQc,gBAAgB,EAAE;gBACvChB,sBAAsB;oBAACiB,SAAS;oBAAOX;gBAAM;YAC/C;QACF;QACAX,2BAA2B,CAACuB,WAAavB,0BAA0BuB,UAAUX,SAAS;gBAACI;YAAK;QAC5FV,mBAAmB,IAAMA,kBAAkBE,WAAW,OAAOG;QAC7Da,OAAOX,KAAKW;QACZC,SAASV,YACL;YACEW,QAAQX,UAAUW,MAAM;YACxBC,UAAUZ,UAAUY,QAAQ;YAC5BC,OAAOb,UAAUa,KAAK;QACxB,IACAd;QACJe,gBAAgB,CAAC,CAACd;QAClBe,QAAQrB,MAAMqB,MAAM;QACpBpB;QACAC;QACAoB,eAAevB,aAAa,mBAAmBA,YAAYA,UAAUuB,aAAa,GAAGjB;QACrFkB,kBAAkBxB,WAAWwB;QAC7BC,WAAWC,QAAQzB,KAAK,CAAC,cAAc;QACvC0B,OAAO1B,MAAM0B,KAAK;QAClBC,gBAAgB3B,MAAM4B,GAAG;QACzBC,MAAM9B,UAAU8B,IAAI;QACpBC,gBAAgBxB,WAAWyB;QAC3B5B;IACF;AACF"}
@@ -1,7 +1,6 @@
1
- import { buildStudio as internalBuildStudio } from '@sanity/cli-build/_internal/build';
1
+ import { compareDependencyVersions, buildStudio as internalBuildStudio } from '@sanity/cli-build/_internal/build';
2
2
  import { resolveWorkbenchApp } from '@sanity/workbench-cli/build';
3
3
  import { getAppId } from '../../util/appId.js';
4
- import { compareDependencyVersions } from '../../util/compareDependencyVersions.js';
5
4
  import { determineIsApp } from '../../util/determineIsApp.js';
6
5
  import { getPackageManagerChoice } from '../../util/packageManager/packageManagerChoice.js';
7
6
  import { upgradePackages } from '../../util/packageManager/upgradePackages.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/build/buildStudio.ts"],"sourcesContent":["import {buildStudio as internalBuildStudio} from '@sanity/cli-build/_internal/build'\nimport {resolveWorkbenchApp} from '@sanity/workbench-cli/build'\n\nimport {getAppId} from '../../util/appId.js'\nimport {compareDependencyVersions} from '../../util/compareDependencyVersions.js'\nimport {determineIsApp} from '../../util/determineIsApp.js'\nimport {getPackageManagerChoice} from '../../util/packageManager/packageManagerChoice.js'\nimport {upgradePackages} from '../../util/packageManager/upgradePackages.js'\nimport {warnAboutMissingAppId} from '../../util/warnAboutMissingAppId.js'\nimport {determineBasePath} from './determineBasePath.js'\nimport {type BuildOptions} from './types.js'\n\n/**\n * Build the Sanity Studio.\n *\n * @internal\n */\nexport async function buildStudio(options: BuildOptions): Promise<void> {\n const {calledFromDeploy, cliConfig, flags, outDir, output, workDir} = options\n\n // `views`/`services` live on the branded `unstable_defineApp` result — resolve\n // the workbench capability so it's gated on the brand, like the app build.\n const workbench = resolveWorkbenchApp(cliConfig)\n\n const appId = getAppId(cliConfig)\n\n const upgradePkgs = async (options: {\n packages: [name: string, version: string][]\n }): Promise<void> => {\n await upgradePackages(\n {\n packageManager: (await getPackageManagerChoice(workDir, {interactive: false})).chosen,\n packages: options.packages,\n },\n {output, workDir},\n )\n }\n\n await internalBuildStudio({\n appId,\n autoUpdatesEnabled: options.autoUpdatesEnabled,\n checkAppId: () => {\n // Warn if auto updates enabled but no appId configured.\n // Skip when called from deploy, since deploy handles appId itself\n // (prompts the user and tells them to add it to config).\n if (!appId && !calledFromDeploy) {\n warnAboutMissingAppId({appType: 'studio', output, projectId: cliConfig?.api?.projectId})\n }\n },\n compareDependencyVersions: (packages) => compareDependencyVersions(packages, workDir, {appId}),\n determineBasePath: () => determineBasePath(cliConfig, 'studio', output),\n exposes: workbench ? {services: workbench.services, views: workbench.views} : undefined,\n isApp: determineIsApp(cliConfig),\n isWorkbenchApp: !!workbench,\n minify: Boolean(flags.minify),\n outDir,\n output,\n reactCompiler: cliConfig.reactCompiler,\n schemaExtraction: cliConfig.schemaExtraction,\n sourceMap: Boolean(flags['source-maps']),\n stats: flags.stats,\n unattendedMode: Boolean(flags.yes),\n upgradePackages: upgradePkgs,\n vite: cliConfig.vite,\n workbenchAppId: workbench?.name,\n workDir,\n })\n}\n"],"names":["buildStudio","internalBuildStudio","resolveWorkbenchApp","getAppId","compareDependencyVersions","determineIsApp","getPackageManagerChoice","upgradePackages","warnAboutMissingAppId","determineBasePath","options","calledFromDeploy","cliConfig","flags","outDir","output","workDir","workbench","appId","upgradePkgs","packageManager","interactive","chosen","packages","autoUpdatesEnabled","checkAppId","appType","projectId","api","exposes","services","views","undefined","isApp","isWorkbenchApp","minify","Boolean","reactCompiler","schemaExtraction","sourceMap","stats","unattendedMode","yes","vite","workbenchAppId","name"],"mappings":"AAAA,SAAQA,eAAeC,mBAAmB,QAAO,oCAAmC;AACpF,SAAQC,mBAAmB,QAAO,8BAA6B;AAE/D,SAAQC,QAAQ,QAAO,sBAAqB;AAC5C,SAAQC,yBAAyB,QAAO,0CAAyC;AACjF,SAAQC,cAAc,QAAO,+BAA8B;AAC3D,SAAQC,uBAAuB,QAAO,oDAAmD;AACzF,SAAQC,eAAe,QAAO,+CAA8C;AAC5E,SAAQC,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,iBAAiB,QAAO,yBAAwB;AAGxD;;;;CAIC,GACD,OAAO,eAAeT,YAAYU,OAAqB;IACrD,MAAM,EAACC,gBAAgB,EAAEC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGN;IAEtE,+EAA+E;IAC/E,2EAA2E;IAC3E,MAAMO,YAAYf,oBAAoBU;IAEtC,MAAMM,QAAQf,SAASS;IAEvB,MAAMO,cAAc,OAAOT;QAGzB,MAAMH,gBACJ;YACEa,gBAAgB,AAAC,CAAA,MAAMd,wBAAwBU,SAAS;gBAACK,aAAa;YAAK,EAAC,EAAGC,MAAM;YACrFC,UAAUb,QAAQa,QAAQ;QAC5B,GACA;YAACR;YAAQC;QAAO;IAEpB;IAEA,MAAMf,oBAAoB;QACxBiB;QACAM,oBAAoBd,QAAQc,kBAAkB;QAC9CC,YAAY;YACV,wDAAwD;YACxD,kEAAkE;YAClE,yDAAyD;YACzD,IAAI,CAACP,SAAS,CAACP,kBAAkB;gBAC/BH,sBAAsB;oBAACkB,SAAS;oBAAUX;oBAAQY,WAAWf,WAAWgB,KAAKD;gBAAS;YACxF;QACF;QACAvB,2BAA2B,CAACmB,WAAanB,0BAA0BmB,UAAUP,SAAS;gBAACE;YAAK;QAC5FT,mBAAmB,IAAMA,kBAAkBG,WAAW,UAAUG;QAChEc,SAASZ,YAAY;YAACa,UAAUb,UAAUa,QAAQ;YAAEC,OAAOd,UAAUc,KAAK;QAAA,IAAIC;QAC9EC,OAAO5B,eAAeO;QACtBsB,gBAAgB,CAAC,CAACjB;QAClBkB,QAAQC,QAAQvB,MAAMsB,MAAM;QAC5BrB;QACAC;QACAsB,eAAezB,UAAUyB,aAAa;QACtCC,kBAAkB1B,UAAU0B,gBAAgB;QAC5CC,WAAWH,QAAQvB,KAAK,CAAC,cAAc;QACvC2B,OAAO3B,MAAM2B,KAAK;QAClBC,gBAAgBL,QAAQvB,MAAM6B,GAAG;QACjCnC,iBAAiBY;QACjBwB,MAAM/B,UAAU+B,IAAI;QACpBC,gBAAgB3B,WAAW4B;QAC3B7B;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/build/buildStudio.ts"],"sourcesContent":["import {\n compareDependencyVersions,\n buildStudio as internalBuildStudio,\n} from '@sanity/cli-build/_internal/build'\nimport {resolveWorkbenchApp} from '@sanity/workbench-cli/build'\n\nimport {getAppId} from '../../util/appId.js'\nimport {determineIsApp} from '../../util/determineIsApp.js'\nimport {getPackageManagerChoice} from '../../util/packageManager/packageManagerChoice.js'\nimport {upgradePackages} from '../../util/packageManager/upgradePackages.js'\nimport {warnAboutMissingAppId} from '../../util/warnAboutMissingAppId.js'\nimport {determineBasePath} from './determineBasePath.js'\nimport {type BuildOptions} from './types.js'\n\n/**\n * Build the Sanity Studio.\n *\n * @internal\n */\nexport async function buildStudio(options: BuildOptions): Promise<void> {\n const {calledFromDeploy, cliConfig, flags, outDir, output, workDir} = options\n\n // `views`/`services` live on the branded `unstable_defineApp` result — resolve\n // the workbench capability so it's gated on the brand, like the app build.\n const workbench = resolveWorkbenchApp(cliConfig)\n\n const appId = getAppId(cliConfig)\n\n const upgradePkgs = async (options: {\n packages: [name: string, version: string][]\n }): Promise<void> => {\n await upgradePackages(\n {\n packageManager: (await getPackageManagerChoice(workDir, {interactive: false})).chosen,\n packages: options.packages,\n },\n {output, workDir},\n )\n }\n\n await internalBuildStudio({\n appId,\n autoUpdatesEnabled: options.autoUpdatesEnabled,\n checkAppId: () => {\n // Warn if auto updates enabled but no appId configured.\n // Skip when called from deploy, since deploy handles appId itself\n // (prompts the user and tells them to add it to config).\n if (!appId && !calledFromDeploy) {\n warnAboutMissingAppId({appType: 'studio', output, projectId: cliConfig?.api?.projectId})\n }\n },\n compareDependencyVersions: (packages) => compareDependencyVersions(packages, workDir, {appId}),\n determineBasePath: () => determineBasePath(cliConfig, 'studio', output),\n exposes: workbench ? {services: workbench.services, views: workbench.views} : undefined,\n isApp: determineIsApp(cliConfig),\n isWorkbenchApp: !!workbench,\n minify: Boolean(flags.minify),\n outDir,\n output,\n reactCompiler: cliConfig.reactCompiler,\n schemaExtraction: cliConfig.schemaExtraction,\n sourceMap: Boolean(flags['source-maps']),\n stats: flags.stats,\n unattendedMode: Boolean(flags.yes),\n upgradePackages: upgradePkgs,\n vite: cliConfig.vite,\n workbenchAppId: workbench?.name,\n workDir,\n })\n}\n"],"names":["compareDependencyVersions","buildStudio","internalBuildStudio","resolveWorkbenchApp","getAppId","determineIsApp","getPackageManagerChoice","upgradePackages","warnAboutMissingAppId","determineBasePath","options","calledFromDeploy","cliConfig","flags","outDir","output","workDir","workbench","appId","upgradePkgs","packageManager","interactive","chosen","packages","autoUpdatesEnabled","checkAppId","appType","projectId","api","exposes","services","views","undefined","isApp","isWorkbenchApp","minify","Boolean","reactCompiler","schemaExtraction","sourceMap","stats","unattendedMode","yes","vite","workbenchAppId","name"],"mappings":"AAAA,SACEA,yBAAyB,EACzBC,eAAeC,mBAAmB,QAC7B,oCAAmC;AAC1C,SAAQC,mBAAmB,QAAO,8BAA6B;AAE/D,SAAQC,QAAQ,QAAO,sBAAqB;AAC5C,SAAQC,cAAc,QAAO,+BAA8B;AAC3D,SAAQC,uBAAuB,QAAO,oDAAmD;AACzF,SAAQC,eAAe,QAAO,+CAA8C;AAC5E,SAAQC,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,iBAAiB,QAAO,yBAAwB;AAGxD;;;;CAIC,GACD,OAAO,eAAeR,YAAYS,OAAqB;IACrD,MAAM,EAACC,gBAAgB,EAAEC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGN;IAEtE,+EAA+E;IAC/E,2EAA2E;IAC3E,MAAMO,YAAYd,oBAAoBS;IAEtC,MAAMM,QAAQd,SAASQ;IAEvB,MAAMO,cAAc,OAAOT;QAGzB,MAAMH,gBACJ;YACEa,gBAAgB,AAAC,CAAA,MAAMd,wBAAwBU,SAAS;gBAACK,aAAa;YAAK,EAAC,EAAGC,MAAM;YACrFC,UAAUb,QAAQa,QAAQ;QAC5B,GACA;YAACR;YAAQC;QAAO;IAEpB;IAEA,MAAMd,oBAAoB;QACxBgB;QACAM,oBAAoBd,QAAQc,kBAAkB;QAC9CC,YAAY;YACV,wDAAwD;YACxD,kEAAkE;YAClE,yDAAyD;YACzD,IAAI,CAACP,SAAS,CAACP,kBAAkB;gBAC/BH,sBAAsB;oBAACkB,SAAS;oBAAUX;oBAAQY,WAAWf,WAAWgB,KAAKD;gBAAS;YACxF;QACF;QACA3B,2BAA2B,CAACuB,WAAavB,0BAA0BuB,UAAUP,SAAS;gBAACE;YAAK;QAC5FT,mBAAmB,IAAMA,kBAAkBG,WAAW,UAAUG;QAChEc,SAASZ,YAAY;YAACa,UAAUb,UAAUa,QAAQ;YAAEC,OAAOd,UAAUc,KAAK;QAAA,IAAIC;QAC9EC,OAAO5B,eAAeO;QACtBsB,gBAAgB,CAAC,CAACjB;QAClBkB,QAAQC,QAAQvB,MAAMsB,MAAM;QAC5BrB;QACAC;QACAsB,eAAezB,UAAUyB,aAAa;QACtCC,kBAAkB1B,UAAU0B,gBAAgB;QAC5CC,WAAWH,QAAQvB,KAAK,CAAC,cAAc;QACvC2B,OAAO3B,MAAM2B,KAAK;QAClBC,gBAAgBL,QAAQvB,MAAM6B,GAAG;QACjCnC,iBAAiBY;QACjBwB,MAAM/B,UAAU+B,IAAI;QACpBC,gBAAgB3B,WAAW4B;QAC3B7B;IACF;AACF"}
@@ -5,6 +5,13 @@ import { createUserApplication as createUserApplicationRequest } from '../../ser
5
5
  import { NO_ORGANIZATION_ID } from '../../util/errorMessages.js';
6
6
  import { deployDebug } from './deployDebug.js';
7
7
  import { normalizeUrl, validateUrl } from './urlUtils.js';
8
+ const LETTERS = 'abcdefghijklmnopqrstuvwxyz';
9
+ // appHosts have some restrictions (no uppercase, must start with a letter)
10
+ export function generateAppSlug() {
11
+ const firstChar = customAlphabet(LETTERS, 1)();
12
+ const rest = customAlphabet(`${LETTERS}0123456789`, 11)();
13
+ return `${firstChar}${rest}`;
14
+ }
8
15
  // TODO: replace with `Promise.withResolvers()` once it lands in node 22
9
16
  function promiseWithResolvers() {
10
17
  let resolve;
@@ -81,16 +88,9 @@ export async function createUserApplication(organizationId, title) {
81
88
  });
82
89
  return tryCreateApp(resolvedTitle, organizationId);
83
90
  }
84
- // appHosts have some restrictions (no uppercase, must start with a letter)
85
- const generateId = ()=>{
86
- const letters = 'abcdefghijklmnopqrstuvwxyz';
87
- const firstChar = customAlphabet(letters, 1)();
88
- const rest = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 11)();
89
- return `${firstChar}${rest}`;
90
- };
91
91
  const tryCreateApp = async (title, organizationId)=>{
92
92
  // we will likely prepend this with an org ID or other parameter in the future
93
- const appHost = generateId();
93
+ const appHost = generateAppSlug();
94
94
  const spin = spinner('Creating application').start();
95
95
  try {
96
96
  const response = await createUserApplicationRequest({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/createUserApplication.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {input, spinner} from '@sanity/cli-core/ux'\nimport {customAlphabet} from 'nanoid'\n\nimport {\n createUserApplication as createUserApplicationRequest,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {NO_ORGANIZATION_ID} from '../../util/errorMessages.js'\nimport {deployDebug} from './deployDebug.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\n// TODO: replace with `Promise.withResolvers()` once it lands in node 22\nfunction promiseWithResolvers<T>() {\n let resolve!: (t: T) => void\n let reject!: (err: unknown) => void\n const promise = new Promise<T>((res, rej) => {\n resolve = res\n reject = rej\n })\n return {promise, reject, resolve}\n}\n\ninterface CreateStudioUserApplicationOptions {\n projectId: string\n\n title?: string\n urlType?: 'external' | 'internal'\n}\n\nexport async function createStudioUserApplication(options: CreateStudioUserApplicationOptions) {\n const {projectId, title, urlType = 'internal'} = options\n const {promise, resolve} = promiseWithResolvers<UserApplication>()\n\n const isExternal = urlType === 'external'\n\n await input({\n message: isExternal ? 'Studio URL (https://...):' : 'Studio hostname (<value>.sanity.studio):',\n // if a string is returned here, it is relayed to the user and prompt allows\n // the user to try again until this function returns true\n validate: async (inp: string) => {\n let appHost: string\n\n if (isExternal) {\n const normalized = normalizeUrl(inp)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return validation\n }\n appHost = normalized\n } else {\n appHost = inp.replace(/\\.sanity\\.studio$/i, '')\n }\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'studio',\n body: {appHost, title, type: 'studio', urlType},\n projectId,\n })\n resolve(response)\n return true\n } catch (e) {\n // if the name is taken, it should return a 409 so we relay to the user\n if ([402, 409].includes(e?.statusCode)) {\n return e?.response?.body?.message || 'Bad request' // just in case\n }\n\n deployDebug('Error creating user application', e)\n // otherwise, it's a fatal error\n throw new CLIError('Error creating user application', {exit: 1})\n }\n },\n })\n\n return await promise\n}\n\nexport async function createUserApplication(\n organizationId?: string,\n title?: string,\n): Promise<UserApplicationResolved> {\n if (!organizationId) {\n throw new Error(NO_ORGANIZATION_ID)\n }\n\n const resolvedTitle =\n title ??\n (await input({\n message: 'Enter a title for your application:',\n validate: (value: string) => value.length > 0 || 'Title is required',\n }))\n\n return tryCreateApp(resolvedTitle, organizationId)\n}\n\n// appHosts have some restrictions (no uppercase, must start with a letter)\nconst generateId = () => {\n const letters = 'abcdefghijklmnopqrstuvwxyz'\n const firstChar = customAlphabet(letters, 1)()\n const rest = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 11)()\n return `${firstChar}${rest}`\n}\n\nconst tryCreateApp = async (title: string, organizationId: string) => {\n // we will likely prepend this with an org ID or other parameter in the future\n const appHost = generateId()\n\n const spin = spinner('Creating application').start()\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'coreApp',\n body: {appHost, title, type: 'coreApp', urlType: 'internal'},\n organizationId,\n })\n\n spin.succeed()\n return response\n } catch (e) {\n // if the name is taken, generate a new one and try again\n if ([402, 409].includes(e?.statusCode)) {\n deployDebug('App host taken, retrying with new host')\n return tryCreateApp(title, organizationId)\n }\n\n spin.fail()\n\n deployDebug('Error creating core application', e)\n throw e\n }\n}\n"],"names":["CLIError","input","spinner","customAlphabet","createUserApplication","createUserApplicationRequest","NO_ORGANIZATION_ID","deployDebug","normalizeUrl","validateUrl","promiseWithResolvers","resolve","reject","promise","Promise","res","rej","createStudioUserApplication","options","projectId","title","urlType","isExternal","message","validate","inp","appHost","normalized","validation","replace","response","appType","body","type","e","includes","statusCode","exit","organizationId","Error","resolvedTitle","value","length","tryCreateApp","generateId","letters","firstChar","rest","spin","start","succeed","fail"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,KAAK,EAAEC,OAAO,QAAO,sBAAqB;AAClD,SAAQC,cAAc,QAAO,SAAQ;AAErC,SACEC,yBAAyBC,4BAA4B,QAGhD,qCAAoC;AAC3C,SAAQC,kBAAkB,QAAO,8BAA6B;AAC9D,SAAQC,WAAW,QAAO,mBAAkB;AAC5C,SAAQC,YAAY,EAAEC,WAAW,QAAO,gBAAe;AAEvD,wEAAwE;AACxE,SAASC;IACP,IAAIC;IACJ,IAAIC;IACJ,MAAMC,UAAU,IAAIC,QAAW,CAACC,KAAKC;QACnCL,UAAUI;QACVH,SAASI;IACX;IACA,OAAO;QAACH;QAASD;QAAQD;IAAO;AAClC;AASA,OAAO,eAAeM,4BAA4BC,OAA2C;IAC3F,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,UAAU,UAAU,EAAC,GAAGH;IACjD,MAAM,EAACL,OAAO,EAAEF,OAAO,EAAC,GAAGD;IAE3B,MAAMY,aAAaD,YAAY;IAE/B,MAAMpB,MAAM;QACVsB,SAASD,aAAa,8BAA8B;QACpD,4EAA4E;QAC5E,yDAAyD;QACzDE,UAAU,OAAOC;YACf,IAAIC;YAEJ,IAAIJ,YAAY;gBACd,MAAMK,aAAanB,aAAaiB;gBAChC,MAAMG,aAAanB,YAAYkB;gBAC/B,IAAIC,eAAe,MAAM;oBACvB,OAAOA;gBACT;gBACAF,UAAUC;YACZ,OAAO;gBACLD,UAAUD,IAAII,OAAO,CAAC,sBAAsB;YAC9C;YAEA,IAAI;gBACF,MAAMC,WAAW,MAAMzB,6BAA6B;oBAClD0B,SAAS;oBACTC,MAAM;wBAACN;wBAASN;wBAAOa,MAAM;wBAAUZ;oBAAO;oBAC9CF;gBACF;gBACAR,QAAQmB;gBACR,OAAO;YACT,EAAE,OAAOI,GAAG;gBACV,uEAAuE;gBACvE,IAAI;oBAAC;oBAAK;iBAAI,CAACC,QAAQ,CAACD,GAAGE,aAAa;oBACtC,OAAOF,GAAGJ,UAAUE,MAAMT,WAAW,cAAc,eAAe;;gBACpE;gBAEAhB,YAAY,mCAAmC2B;gBAC/C,gCAAgC;gBAChC,MAAM,IAAIlC,SAAS,mCAAmC;oBAACqC,MAAM;gBAAC;YAChE;QACF;IACF;IAEA,OAAO,MAAMxB;AACf;AAEA,OAAO,eAAeT,sBACpBkC,cAAuB,EACvBlB,KAAc;IAEd,IAAI,CAACkB,gBAAgB;QACnB,MAAM,IAAIC,MAAMjC;IAClB;IAEA,MAAMkC,gBACJpB,SACC,MAAMnB,MAAM;QACXsB,SAAS;QACTC,UAAU,CAACiB,QAAkBA,MAAMC,MAAM,GAAG,KAAK;IACnD;IAEF,OAAOC,aAAaH,eAAeF;AACrC;AAEA,2EAA2E;AAC3E,MAAMM,aAAa;IACjB,MAAMC,UAAU;IAChB,MAAMC,YAAY3C,eAAe0C,SAAS;IAC1C,MAAME,OAAO5C,eAAe,wCAAwC;IACpE,OAAO,GAAG2C,YAAYC,MAAM;AAC9B;AAEA,MAAMJ,eAAe,OAAOvB,OAAekB;IACzC,8EAA8E;IAC9E,MAAMZ,UAAUkB;IAEhB,MAAMI,OAAO9C,QAAQ,wBAAwB+C,KAAK;IAElD,IAAI;QACF,MAAMnB,WAAW,MAAMzB,6BAA6B;YAClD0B,SAAS;YACTC,MAAM;gBAACN;gBAASN;gBAAOa,MAAM;gBAAWZ,SAAS;YAAU;YAC3DiB;QACF;QAEAU,KAAKE,OAAO;QACZ,OAAOpB;IACT,EAAE,OAAOI,GAAG;QACV,yDAAyD;QACzD,IAAI;YAAC;YAAK;SAAI,CAACC,QAAQ,CAACD,GAAGE,aAAa;YACtC7B,YAAY;YACZ,OAAOoC,aAAavB,OAAOkB;QAC7B;QAEAU,KAAKG,IAAI;QAET5C,YAAY,mCAAmC2B;QAC/C,MAAMA;IACR;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/createUserApplication.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {input, spinner} from '@sanity/cli-core/ux'\nimport {customAlphabet} from 'nanoid'\n\nimport {\n createUserApplication as createUserApplicationRequest,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {NO_ORGANIZATION_ID} from '../../util/errorMessages.js'\nimport {deployDebug} from './deployDebug.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\nconst LETTERS = 'abcdefghijklmnopqrstuvwxyz'\n\n// appHosts have some restrictions (no uppercase, must start with a letter)\nexport function generateAppSlug(): string {\n const firstChar = customAlphabet(LETTERS, 1)()\n const rest = customAlphabet(`${LETTERS}0123456789`, 11)()\n return `${firstChar}${rest}`\n}\n\n// TODO: replace with `Promise.withResolvers()` once it lands in node 22\nfunction promiseWithResolvers<T>() {\n let resolve!: (t: T) => void\n let reject!: (err: unknown) => void\n const promise = new Promise<T>((res, rej) => {\n resolve = res\n reject = rej\n })\n return {promise, reject, resolve}\n}\n\ninterface CreateStudioUserApplicationOptions {\n projectId: string\n\n title?: string\n urlType?: 'external' | 'internal'\n}\n\nexport async function createStudioUserApplication(options: CreateStudioUserApplicationOptions) {\n const {projectId, title, urlType = 'internal'} = options\n const {promise, resolve} = promiseWithResolvers<UserApplication>()\n\n const isExternal = urlType === 'external'\n\n await input({\n message: isExternal ? 'Studio URL (https://...):' : 'Studio hostname (<value>.sanity.studio):',\n // if a string is returned here, it is relayed to the user and prompt allows\n // the user to try again until this function returns true\n validate: async (inp: string) => {\n let appHost: string\n\n if (isExternal) {\n const normalized = normalizeUrl(inp)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return validation\n }\n appHost = normalized\n } else {\n appHost = inp.replace(/\\.sanity\\.studio$/i, '')\n }\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'studio',\n body: {appHost, title, type: 'studio', urlType},\n projectId,\n })\n resolve(response)\n return true\n } catch (e) {\n // if the name is taken, it should return a 409 so we relay to the user\n if ([402, 409].includes(e?.statusCode)) {\n return e?.response?.body?.message || 'Bad request' // just in case\n }\n\n deployDebug('Error creating user application', e)\n // otherwise, it's a fatal error\n throw new CLIError('Error creating user application', {exit: 1})\n }\n },\n })\n\n return await promise\n}\n\nexport async function createUserApplication(\n organizationId?: string,\n title?: string,\n): Promise<UserApplicationResolved> {\n if (!organizationId) {\n throw new Error(NO_ORGANIZATION_ID)\n }\n\n const resolvedTitle =\n title ??\n (await input({\n message: 'Enter a title for your application:',\n validate: (value: string) => value.length > 0 || 'Title is required',\n }))\n\n return tryCreateApp(resolvedTitle, organizationId)\n}\n\nconst tryCreateApp = async (title: string, organizationId: string) => {\n // we will likely prepend this with an org ID or other parameter in the future\n const appHost = generateAppSlug()\n\n const spin = spinner('Creating application').start()\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'coreApp',\n body: {appHost, title, type: 'coreApp', urlType: 'internal'},\n organizationId,\n })\n\n spin.succeed()\n return response\n } catch (e) {\n // if the name is taken, generate a new one and try again\n if ([402, 409].includes(e?.statusCode)) {\n deployDebug('App host taken, retrying with new host')\n return tryCreateApp(title, organizationId)\n }\n\n spin.fail()\n\n deployDebug('Error creating core application', e)\n throw e\n }\n}\n"],"names":["CLIError","input","spinner","customAlphabet","createUserApplication","createUserApplicationRequest","NO_ORGANIZATION_ID","deployDebug","normalizeUrl","validateUrl","LETTERS","generateAppSlug","firstChar","rest","promiseWithResolvers","resolve","reject","promise","Promise","res","rej","createStudioUserApplication","options","projectId","title","urlType","isExternal","message","validate","inp","appHost","normalized","validation","replace","response","appType","body","type","e","includes","statusCode","exit","organizationId","Error","resolvedTitle","value","length","tryCreateApp","spin","start","succeed","fail"],"mappings":"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"}
@@ -2,19 +2,19 @@ import { basename, dirname } from 'node:path';
2
2
  import { styleText } from 'node:util';
3
3
  import { createGzip } from 'node:zlib';
4
4
  import { exitCodes } from '@sanity/cli-core';
5
+ import { getErrorMessage } from '@sanity/cli-core/errors';
5
6
  import { spinner } from '@sanity/cli-core/ux';
6
- import { deployInstallationConfig, getWorkbench, resolveInstallationId, summarizeInstallationConfig } from '@sanity/workbench-cli/deploy';
7
+ import { buildExposes, deployConfig, deployCoreApp as deployWorkbenchCoreApp, getWorkbench, resolveInstallationId, summarizeConfig } from '@sanity/workbench-cli/deploy';
7
8
  import { pack } from 'tar-fs';
8
9
  import { createDeployment, updateUserApplication } from '../../services/userApplications.js';
9
10
  import { getAppId } from '../../util/appId.js';
10
11
  import { EXTERNAL_APP_NOT_SUPPORTED, NO_ORGANIZATION_ID } from '../../util/errorMessages.js';
11
- import { getErrorMessage } from '../../util/getErrorMessage.js';
12
12
  import { buildApp } from '../build/buildApp.js';
13
13
  import { extractCoreAppManifest, resolveTitleUpdate } from '../manifest/extractCoreAppManifest.js';
14
- import { createUserApplication } from './createUserApplication.js';
14
+ import { createUserApplication, generateAppSlug } from './createUserApplication.js';
15
15
  import { checkAppId, checkAppTarget, checkAutoUpdates, checkBuild, checkPackageVersion, verifyOutputDir } from './deployChecks.js';
16
16
  import { deployDebug } from './deployDebug.js';
17
- import { listDeploymentFiles } from './deploymentPlan.js';
17
+ import { listDeploymentFiles, reportExposes } from './deploymentPlan.js';
18
18
  import { runDeploy } from './deployRunner.js';
19
19
  import { findUserApplication } from './findUserApplication.js';
20
20
  import { getCoreAppUrl } from './urlUtils.js';
@@ -30,12 +30,14 @@ export function deployApp(options) {
30
30
  const { cliConfig, flags, output, sourceDir } = options;
31
31
  const workDir = options.projectRoot.directory;
32
32
  const organizationId = cliConfig.app?.organizationId;
33
+ const appId = getAppId(cliConfig);
33
34
  const workbench = getWorkbench(cliConfig);
34
35
  const dryRun = !!flags['dry-run'];
35
36
  // A singleton (the Media Library) persists its config instead of hosting an
36
- // application; anything with interfaces still ships one.
37
- const deploySingletonInstallationConfig = workbench?.deploySingletonInstallationConfig ?? false;
37
+ // application; anything that exposes a view or service still ships one.
38
+ const deploySingletonConfig = workbench?.deploySingletonConfig ?? false;
38
39
  const deployApplication = !workbench || workbench.hasInterfaces;
40
+ const appTitle = workbench ? flags.title?.trim() || cliConfig.app?.title?.trim() || workbench.name : '';
39
41
  // A federated app with no entry, view or service would ship a remote with
40
42
  // nothing to load — reported first so it fails before any prompt or API call.
41
43
  if (workbench) {
@@ -62,7 +64,7 @@ export function deployApp(options) {
62
64
  moduleName: APP_PACKAGE,
63
65
  workDir
64
66
  });
65
- } else if (deploySingletonInstallationConfig) {
67
+ } else if (deploySingletonConfig) {
66
68
  version = await checkPackageVersion(reporter, {
67
69
  moduleName: 'sanity',
68
70
  workDir
@@ -80,17 +82,27 @@ export function deployApp(options) {
80
82
  cliConfig
81
83
  });
82
84
  let application = null;
85
+ let appCreated = false;
83
86
  if (flags.external) {
84
87
  reporter.report({
85
88
  message: EXTERNAL_APP_NOT_SUPPORTED,
86
89
  solution: 'Remove the --external flag — apps deploy to Sanity hosting',
87
90
  status: 'fail'
88
91
  });
92
+ } else if (deployApplication && workbench) {
93
+ // Both modes, so a bad appId fails before the build rather than at the POST.
94
+ await checkAppTarget(reporter, {
95
+ appId,
96
+ isWorkbenchApp: true,
97
+ slug: workbench.slug,
98
+ title: appTitle
99
+ });
89
100
  } else if (deployApplication) {
90
- application = await resolveAppApplication(options, {
101
+ ;
102
+ ({ application, created: appCreated } = await resolveAppApplication(options, {
91
103
  dryRun,
92
104
  reporter
93
- });
105
+ }));
94
106
  }
95
107
  await checkBuild(reporter, {
96
108
  build: ()=>buildApp({
@@ -126,14 +138,17 @@ export function deployApp(options) {
126
138
  // Resolve the installation in both modes so the report — dry-run and real —
127
139
  // shows whether the config is deployable; a missing one fails the deploy here.
128
140
  let installationId;
129
- const configAppType = workbench?.installationConfig?.appType;
130
- if (deploySingletonInstallationConfig && organizationId && workbench?.installationConfig && configAppType) {
141
+ let config;
142
+ const configAppType = workbench?.config?.appType;
143
+ if (deploySingletonConfig && organizationId && workbench?.config && configAppType) {
131
144
  installationId = await resolveInstallationId({
132
145
  appType: configAppType,
133
146
  organizationId
134
147
  });
148
+ config = summarizeConfig(workbench.config);
135
149
  reporter.report(installationId ? {
136
- message: summarizeInstallationConfig(workbench.installationConfig),
150
+ config,
151
+ message: config,
137
152
  status: 'pass'
138
153
  } : {
139
154
  exitCode: exitCodes.USAGE_ERROR,
@@ -142,10 +157,20 @@ export function deployApp(options) {
142
157
  status: 'fail'
143
158
  });
144
159
  }
160
+ // Report the exposes deploying with the application, both modes.
161
+ const exposes = deployApplication && workbench ? reportExposes(reporter, workbench) : [];
162
+ // Surface the app's explicit singleton flag when set, both modes.
163
+ if (deployApplication && workbench?.isSingleton !== undefined) {
164
+ reporter.report({
165
+ isSingleton: workbench.isSingleton,
166
+ message: `Singleton: ${workbench.isSingleton}`,
167
+ status: 'pass'
168
+ });
169
+ }
145
170
  // Dry run stops here — everything below mutates.
146
171
  if (dryRun) return;
147
172
  if (installationId && version && configAppType) {
148
- await deployInstallationConfig({
173
+ await deployConfig({
149
174
  appType: configAppType,
150
175
  installationId,
151
176
  output,
@@ -153,21 +178,75 @@ export function deployApp(options) {
153
178
  version
154
179
  });
155
180
  }
156
- // A config-only singleton ships no application, only its installation config.
181
+ // A config-only singleton ships no application, only its config.
157
182
  if (!deployApplication) {
158
183
  if (installationId && version) {
159
184
  return {
160
185
  applicationType: 'coreApp',
161
186
  applicationVersion: version,
187
+ ...config ? {
188
+ config
189
+ } : {},
162
190
  installationId,
163
191
  target: null
164
192
  };
165
193
  }
166
194
  return;
167
195
  }
196
+ // A real deploy already exited on a version-resolution failure; this narrows the type.
197
+ if (!version) return;
198
+ // Workbench apps deploy to Brett; plain coreApps use user-applications.
199
+ if (workbench && organizationId) {
200
+ const appId = getAppId(cliConfig);
201
+ const slug = workbench.slug ?? generateAppSlug();
202
+ const { applicationId } = await deployWorkbenchCoreApp({
203
+ appId,
204
+ interfaces: buildExposes(workbench, {
205
+ appName: workbench.name,
206
+ appTitle,
207
+ exposesAppView: workbench.entry !== undefined,
208
+ version
209
+ }),
210
+ isAutoUpdating,
211
+ isSingleton: workbench.isSingleton,
212
+ organizationId,
213
+ slug,
214
+ sourceDir,
215
+ title: appTitle,
216
+ version
217
+ });
218
+ logAppDeployed({
219
+ applicationId,
220
+ cliConfig,
221
+ created: !appId,
222
+ organizationId,
223
+ output,
224
+ title: appTitle
225
+ });
226
+ return {
227
+ applicationType: 'coreApp',
228
+ applicationVersion: version,
229
+ ...exposes.length > 0 ? {
230
+ exposes
231
+ } : {},
232
+ ...workbench.isSingleton === undefined ? {} : {
233
+ isSingleton: workbench.isSingleton
234
+ },
235
+ target: {
236
+ action: appId ? 'update' : 'create',
237
+ applicationId,
238
+ // A redeploy ignores the slug, so only a create reports the one it used.
239
+ ...appId ? {} : {
240
+ slug
241
+ },
242
+ title: appTitle,
243
+ url: getCoreAppUrl(organizationId, applicationId)
244
+ }
245
+ };
246
+ }
168
247
  // A real deploy has already exited if anything failed; landing here without a
169
- // resolved application or version means the deploy target was never resolved.
170
- if (!application || !version) return;
248
+ // resolved application means the deploy target was never resolved.
249
+ if (!application) return;
171
250
  application = await syncApplicationTitle({
172
251
  application,
173
252
  manifest,
@@ -181,14 +260,18 @@ export function deployApp(options) {
181
260
  version
182
261
  });
183
262
  logAppDeployed({
184
- application,
263
+ applicationId: application.id,
185
264
  cliConfig,
186
- output
265
+ created: appCreated,
266
+ organizationId: application.organizationId,
267
+ output,
268
+ title: application.title
187
269
  });
188
270
  return {
189
271
  applicationType: 'coreApp',
190
272
  applicationVersion: version,
191
273
  target: {
274
+ action: appCreated ? 'create' : 'update',
192
275
  applicationId: application.id,
193
276
  title: application.title ?? null,
194
277
  url: getCoreAppUrl(application.organizationId, application.id)
@@ -209,7 +292,10 @@ export function deployApp(options) {
209
292
  organizationId,
210
293
  title
211
294
  });
212
- return null;
295
+ return {
296
+ application: null,
297
+ created: false
298
+ };
213
299
  }
214
300
  let application = await findUserApplication({
215
301
  cliConfig,
@@ -223,8 +309,15 @@ export function deployApp(options) {
223
309
  deployDebug('No user application found. Creating a new one');
224
310
  application = await createUserApplication(organizationId, title);
225
311
  deployDebug('User application created', application);
312
+ return {
313
+ application,
314
+ created: true
315
+ };
226
316
  }
227
- return application;
317
+ return {
318
+ application,
319
+ created: false
320
+ };
228
321
  }
229
322
  /** Syncs the application title from the manifest when it has changed. */ async function syncApplicationTitle({ application, manifest, output }) {
230
323
  const titleUpdate = resolveTitleUpdate(manifest, application);
@@ -274,12 +367,16 @@ async function shipAppDeployment({ application, isAutoUpdating, manifest, source
274
367
  }
275
368
  spin.succeed();
276
369
  }
277
- export function logAppDeployed({ application, cliConfig, output }) {
278
- const url = getCoreAppUrl(application.organizationId, application.id);
279
- const named = application.title ? ` — "${application.title}"` : '';
370
+ export function logAppDeployed({ applicationId, cliConfig, created, organizationId, output, title }) {
371
+ const url = getCoreAppUrl(organizationId, applicationId);
372
+ const named = title ? ` — "${title}"` : '';
280
373
  output.log(`\nSuccess! Application deployed to ${styleText('cyan', url)}${named}`);
374
+ output.log(created ? 'Created a new application.' : 'Updated the existing application.');
281
375
  if (getAppId(cliConfig)) return;
282
376
  output.log(`\n════ ${styleText('bold', 'Next step:')} ════`);
377
+ if (created) {
378
+ output.log(styleText('yellow', '\nDeploying again without `deployment.appId` creates another new application.'));
379
+ }
283
380
  output.log(styleText('bold', '\nAdd the deployment.appId to your sanity.cli.js or sanity.cli.ts file:'));
284
381
  output.log(`
285
382
  ${styleText('dim', `app: {
@@ -289,7 +386,7 @@ ${styleText([
289
386
  'bold',
290
387
  'green'
291
388
  ], `deployment: {
292
- appId: '${application.id}',
389
+ appId: '${applicationId}',
293
390
  }\n`)}`);
294
391
  }
295
392