@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
package/README.md CHANGED
@@ -14,6 +14,7 @@ Code for sanity cli
14
14
  <!-- commands -->
15
15
 
16
16
  - [`sanity api ENDPOINT`](#sanity-api-endpoint)
17
+ - [`sanity assets upload`](#sanity-assets-upload)
17
18
  - [`sanity backups disable [DATASET]`](#sanity-backups-disable-dataset)
18
19
  - [`sanity backups download [DATASET]`](#sanity-backups-download-dataset)
19
20
  - [`sanity backups enable [DATASET]`](#sanity-backups-enable-dataset)
@@ -119,6 +120,7 @@ Code for sanity cli
119
120
  - [`sanity tokens create [LABEL]`](#sanity-tokens-create-label)
120
121
  - [`sanity tokens delete [TOKENID]`](#sanity-tokens-delete-tokenid)
121
122
  - [`sanity tokens list`](#sanity-tokens-list)
123
+ - [`sanity tokens rotate`](#sanity-tokens-rotate)
122
124
  - [`sanity typegen generate`](#sanity-typegen-generate)
123
125
  - [`sanity undeploy`](#sanity-undeploy)
124
126
  - [`sanity users invite [EMAIL]`](#sanity-users-invite-email)
@@ -212,6 +214,40 @@ EXAMPLES
212
214
  SANITY_AUTH_TOKEN=<token> sanity api users/me
213
215
  ```
214
216
 
217
+ ## `sanity assets upload`
218
+
219
+ Upload one local image or file to a Sanity dataset and print the asset document as JSON
220
+
221
+ ```
222
+ USAGE
223
+ $ sanity assets upload --file <path> [-p <id>] [-d <name>] [--content-type <mime-type>] [--filename <filename>]
224
+ [--type image|file]
225
+
226
+ FLAGS
227
+ --content-type=<mime-type> MIME type of the asset, such as image/png or application/pdf
228
+ --file=<path> (required) Path to the local file to upload
229
+ --filename=<filename> Original filename stored on the asset document. Defaults to the local filename
230
+ --type=<option> [default: image] Asset type to create
231
+ <options: image|file>
232
+
233
+ OVERRIDE FLAGS
234
+ -d, --dataset=<name> Dataset to upload the asset to (overrides CLI configuration)
235
+ -p, --project-id=<id> Project ID to upload the asset to (overrides CLI configuration)
236
+
237
+ DESCRIPTION
238
+ Upload one local image or file to a Sanity dataset and print the asset document as JSON
239
+
240
+ EXAMPLES
241
+ Upload an image using the configured project
242
+
243
+ $ sanity assets upload --file ./hero.png --type image --dataset production
244
+
245
+ Upload a file with explicit project, dataset, and MIME type
246
+
247
+ $ sanity assets upload --file ./brief.pdf --type file --content-type application/pdf --project-id abc123 \
248
+ --dataset production
249
+ ```
250
+
215
251
  ## `sanity backups disable [DATASET]`
216
252
 
217
253
  Disable backup for a dataset
@@ -2972,13 +3008,14 @@ Create a Sanity project without an account, and claim it within 72 hours to keep
2972
3008
 
2973
3009
  ```
2974
3010
  USAGE
2975
- $ sanity new [PROJECTNAME] [--json] [--scaffold] [-y]
3011
+ $ sanity new [PROJECTNAME] [--json] [--instructions] [--scaffold] [-y]
2976
3012
 
2977
3013
  ARGUMENTS
2978
3014
  [PROJECTNAME] Display name for the new project
2979
3015
 
2980
3016
  FLAGS
2981
3017
  -y, --yes Skip prompts and use defaults (project: "My Sanity project")
3018
+ --instructions Print the full setup guide from https://sanity.new and exit, creating nothing
2982
3019
  --[no-]scaffold Set up a Studio in ./sanity and a Next.js website in ./web (on by default)
2983
3020
 
2984
3021
  GLOBAL FLAGS
@@ -3003,7 +3040,7 @@ DESCRIPTION
3003
3040
  Keep both env files out of git, and never put the token in code that runs in
3004
3041
  the browser.
3005
3042
 
3006
- Fetch https://sanity.new for full instructions, or point your AI agent at it.
3043
+ Run this command with --instructions for the full agent setup guide.
3007
3044
 
3008
3045
  EXAMPLES
3009
3046
  Create a project with a Studio and a website
@@ -3025,6 +3062,10 @@ EXAMPLES
3025
3062
  Create a project and print its details as JSON
3026
3063
 
3027
3064
  $ sanity new --json
3065
+
3066
+ Print the full setup guide for an AI agent, without creating anything
3067
+
3068
+ $ sanity new --instructions
3028
3069
  ```
3029
3070
 
3030
3071
  ## `sanity openapi get SLUG`
@@ -3596,15 +3637,16 @@ Create a new API token for the project
3596
3637
 
3597
3638
  ```
3598
3639
  USAGE
3599
- $ sanity tokens create [LABEL] [-p <id>] [--json] [--role viewer] [-y]
3640
+ $ sanity tokens create [LABEL] [-p <id>] [--expires-at 2027-01-01] [--json] [--role viewer] [-y]
3600
3641
 
3601
3642
  ARGUMENTS
3602
3643
  [LABEL] Label for the new token
3603
3644
 
3604
3645
  FLAGS
3605
- -y, --yes Skip prompts and use defaults (unattended mode)
3606
- --json Output as JSON
3607
- --role=viewer Role to assign to the token (defaults to viewer in unattended mode)
3646
+ -y, --yes Skip prompts and use defaults (unattended mode)
3647
+ --expires-at=2027-01-01 Date or timestamp the token expires (ISO 8601; tokens never expire by default)
3648
+ --json Output as JSON
3649
+ --role=viewer Role to assign to the token (defaults to viewer in unattended mode)
3608
3650
 
3609
3651
  OVERRIDE FLAGS
3610
3652
  -p, --project-id=<id> Project ID to create token in (overrides CLI configuration)
@@ -3625,6 +3667,10 @@ EXAMPLES
3625
3667
 
3626
3668
  $ sanity tokens create "CI Token" --role=editor --yes
3627
3669
 
3670
+ Create a token that expires on a given date
3671
+
3672
+ $ sanity tokens create "Build Token" --expires-at 2027-01-01
3673
+
3628
3674
  Output token information as JSON
3629
3675
 
3630
3676
  $ sanity tokens create "API Token" --json
@@ -3703,6 +3749,35 @@ EXAMPLES
3703
3749
  $ sanity tokens list --project-id abc123
3704
3750
  ```
3705
3751
 
3752
+ ## `sanity tokens rotate`
3753
+
3754
+ Rotate an API token, replacing its secret with a new one
3755
+
3756
+ ```
3757
+ USAGE
3758
+ $ sanity tokens rotate [--json] [-t <token>]
3759
+
3760
+ FLAGS
3761
+ -t, --token=<token> Token to rotate (prefer standard input to keep it out of shell history)
3762
+ --json Output as JSON
3763
+
3764
+ DESCRIPTION
3765
+ Rotate an API token, replacing its secret with a new one
3766
+
3767
+ EXAMPLES
3768
+ Rotate the token piped on standard input
3769
+
3770
+ echo "$SANITY_TOKEN" | sanity tokens rotate
3771
+
3772
+ Rotate a token read from a file
3773
+
3774
+ $ sanity tokens rotate < token.txt
3775
+
3776
+ Output the rotated token as JSON
3777
+
3778
+ echo "$SANITY_TOKEN" | sanity tokens rotate --json
3779
+ ```
3780
+
3706
3781
  ## `sanity typegen generate`
3707
3782
 
3708
3783
  Sanity TypeGen
@@ -1,13 +1,6 @@
1
1
  import { Help } from '@oclif/core';
2
2
  import { getBinCommand, getRunningPackageManager } from '@sanity/cli-core/package-manager';
3
- import { topicAliases } from './topicAliases.js';
4
- // Reverse map: alias name → canonical topic name
5
- const aliasToCanonical = new Map();
6
- for (const [canonical, aliases] of Object.entries(topicAliases)){
7
- for (const alias of aliases){
8
- aliasToCanonical.set(alias, canonical);
9
- }
10
- }
3
+ import { resolveTopicAliasInArgv } from './topicAliases.js';
11
4
  // Running `oclif readme`, we don't want to apply the `prefixBinName` transformation,
12
5
  // as it will include whatever pm was used to spawn the script in the generated readme.
13
6
  // argv will contain something like [nodeBinPath, oclifBinPath, 'readme', …] so check
@@ -88,32 +81,5 @@ function needsFlagSeparator() {
88
81
  const pm = getRunningPackageManager();
89
82
  return pm === 'npm' || !pm;
90
83
  }
91
- /**
92
- * Replace the first positional argument in argv with the canonical topic name
93
- * if it is a known topic alias. This ensures that `sanity dataset --help`
94
- * resolves to the same help output as `sanity datasets --help`.
95
- *
96
- * Without this, `--help` bypasses the command_not_found hook (which normally
97
- * handles alias resolution) and the help system fails to find the topic.
98
- *
99
- * @internal
100
- */ export function resolveTopicAliasInArgv(argv) {
101
- for(let i = 0; i < argv.length; i++){
102
- const arg = argv[i];
103
- if (arg === '--') break;
104
- if (arg.startsWith('-')) continue;
105
- // First positional argument is the topic/command name
106
- const canonical = aliasToCanonical.get(arg);
107
- if (canonical) {
108
- const resolved = [
109
- ...argv
110
- ];
111
- resolved[i] = canonical;
112
- return resolved;
113
- }
114
- break;
115
- }
116
- return argv;
117
- }
118
84
 
119
85
  //# sourceMappingURL=SanityHelp.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/SanityHelp.ts"],"sourcesContent":["import {Command, CommandHelp, Help, Interfaces} from '@oclif/core'\nimport {getBinCommand, getRunningPackageManager} from '@sanity/cli-core/package-manager'\n\nimport {topicAliases} from './topicAliases.js'\n\n// Reverse map: alias name → canonical topic name\nconst aliasToCanonical = new Map<string, string>()\nfor (const [canonical, aliases] of Object.entries(topicAliases)) {\n for (const alias of aliases) {\n aliasToCanonical.set(alias, canonical)\n }\n}\n\n// Running `oclif readme`, we don't want to apply the `prefixBinName` transformation,\n// as it will include whatever pm was used to spawn the script in the generated readme.\n// argv will contain something like [nodeBinPath, oclifBinPath, 'readme', …] so check\n// for 'readme' with a preceeding argument that includes 'oclif' to be sure.\nconst IS_README_GENERATION = (process.argv[process.argv.indexOf('readme') - 1] ?? '').includes(\n 'oclif',\n)\n\n/**\n * Custom Help class for Sanity CLI that overrides the default help formatting to\n * prefix the bin name (e.g., `npx sanity`, `yarn sanity`, etc.) in the help text,\n * and to replace `sanity init` references with the appropriate `create` command\n * for the detected package manager when needed.\n *\n * @internal\n */\nexport default class SanityHelp extends Help {\n protected formatCommand(command: Command.Loadable): string {\n let help = super.formatCommand(command)\n\n // When `sanity init` is called, but originates from the `create-sanity`\n // package/binary (eg the one used by `npm create sanity@latest` etc), we want to\n // customize the help text to show that command instead of `sanity init`.\n const isFromCreate = process.argv.includes('--from-create') && command.id === 'init'\n if (isFromCreate) {\n help = replaceInitWithCreateCommand(help)\n }\n\n return prefixBinName(help)\n }\n\n protected formatRoot(): string {\n return prefixBinName(super.formatRoot())\n }\n\n protected formatTopic(topic: Interfaces.Topic): string {\n return prefixBinName(super.formatTopic(topic))\n }\n\n protected override getCommandHelpClass(command: Command.Loadable): CommandHelp {\n const commandHelp = super.getCommandHelpClass(command)\n if (command.id === 'login') {\n commandHelp.opts = {...commandHelp.opts, flagSortOrder: 'none'}\n }\n return commandHelp\n }\n\n async showHelp(argv: string[]): Promise<void> {\n return super.showHelp(resolveTopicAliasInArgv(argv))\n }\n}\n\n/**\n * @internal\n */\nexport function prefixBinName(help: string): string {\n if (IS_README_GENERATION) return help\n const binCommand = getBinCommand()\n if (binCommand === 'sanity') return help\n return help.replaceAll('$ sanity', `$ ${binCommand}`)\n}\n\n/**\n * Replace `sanity init` references in help text with the equivalent `create` command\n * for the detected package manager. Lines ending in just `sanity init\\n` (no flags)\n * are replaced without a flag separator, while lines with flags get the separator\n * (eg `--` for npm) so the flags are forwarded correctly.\n *\n * @internal\n */\nexport function replaceInitWithCreateCommand(help: string): string {\n const createCmd = guessCreateCommand()\n const flagSeparator = needsFlagSeparator() ? ' --' : ''\n\n // First replace all `sanity init` references that ends with a newline with the\n // create variant that does not include any flag separator (eg `--`). Then replace\n // the other references that do. Most package managers do not require the `--`\n // separator, but npm does. Only include it if we need to, as the commands look\n // cleaner without it.\n return help\n .replaceAll(/(\\s+)sanity\\s+init\\s*\\n/g, `$1${createCmd}\\n`)\n .replaceAll(/(\\s+)sanity(\\s+)init/g, `$1${createCmd}${flagSeparator}`)\n}\n\nfunction guessCreateCommand() {\n const pm = getRunningPackageManager()\n if (pm === 'yarn') return `yarn create sanity`\n if (pm === 'bun') return `bun create sanity@latest`\n if (pm === 'pnpm') return `pnpm create sanity@latest`\n return `npm create sanity@latest`\n}\n\nfunction needsFlagSeparator() {\n const pm = getRunningPackageManager()\n return pm === 'npm' || !pm\n}\n\n/**\n * Replace the first positional argument in argv with the canonical topic name\n * if it is a known topic alias. This ensures that `sanity dataset --help`\n * resolves to the same help output as `sanity datasets --help`.\n *\n * Without this, `--help` bypasses the command_not_found hook (which normally\n * handles alias resolution) and the help system fails to find the topic.\n *\n * @internal\n */\nexport function resolveTopicAliasInArgv(argv: string[]): string[] {\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--') break\n if (arg.startsWith('-')) continue\n\n // First positional argument is the topic/command name\n const canonical = aliasToCanonical.get(arg)\n if (canonical) {\n const resolved = [...argv]\n resolved[i] = canonical\n return resolved\n }\n break\n }\n return argv\n}\n"],"names":["Help","getBinCommand","getRunningPackageManager","topicAliases","aliasToCanonical","Map","canonical","aliases","Object","entries","alias","set","IS_README_GENERATION","process","argv","indexOf","includes","SanityHelp","formatCommand","command","help","isFromCreate","id","replaceInitWithCreateCommand","prefixBinName","formatRoot","formatTopic","topic","getCommandHelpClass","commandHelp","opts","flagSortOrder","showHelp","resolveTopicAliasInArgv","binCommand","replaceAll","createCmd","guessCreateCommand","flagSeparator","needsFlagSeparator","pm","i","length","arg","startsWith","get","resolved"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/SanityHelp.ts"],"sourcesContent":["import {Command, CommandHelp, Help, Interfaces} from '@oclif/core'\nimport {getBinCommand, getRunningPackageManager} from '@sanity/cli-core/package-manager'\n\nimport {resolveTopicAliasInArgv} from './topicAliases.js'\n\n// Running `oclif readme`, we don't want to apply the `prefixBinName` transformation,\n// as it will include whatever pm was used to spawn the script in the generated readme.\n// argv will contain something like [nodeBinPath, oclifBinPath, 'readme', …] so check\n// for 'readme' with a preceeding argument that includes 'oclif' to be sure.\nconst IS_README_GENERATION = (process.argv[process.argv.indexOf('readme') - 1] ?? '').includes(\n 'oclif',\n)\n\n/**\n * Custom Help class for Sanity CLI that overrides the default help formatting to\n * prefix the bin name (e.g., `npx sanity`, `yarn sanity`, etc.) in the help text,\n * and to replace `sanity init` references with the appropriate `create` command\n * for the detected package manager when needed.\n *\n * @internal\n */\nexport default class SanityHelp extends Help {\n protected formatCommand(command: Command.Loadable): string {\n let help = super.formatCommand(command)\n\n // When `sanity init` is called, but originates from the `create-sanity`\n // package/binary (eg the one used by `npm create sanity@latest` etc), we want to\n // customize the help text to show that command instead of `sanity init`.\n const isFromCreate = process.argv.includes('--from-create') && command.id === 'init'\n if (isFromCreate) {\n help = replaceInitWithCreateCommand(help)\n }\n\n return prefixBinName(help)\n }\n\n protected formatRoot(): string {\n return prefixBinName(super.formatRoot())\n }\n\n protected formatTopic(topic: Interfaces.Topic): string {\n return prefixBinName(super.formatTopic(topic))\n }\n\n protected override getCommandHelpClass(command: Command.Loadable): CommandHelp {\n const commandHelp = super.getCommandHelpClass(command)\n if (command.id === 'login') {\n commandHelp.opts = {...commandHelp.opts, flagSortOrder: 'none'}\n }\n return commandHelp\n }\n\n async showHelp(argv: string[]): Promise<void> {\n return super.showHelp(resolveTopicAliasInArgv(argv))\n }\n}\n\n/**\n * @internal\n */\nexport function prefixBinName(help: string): string {\n if (IS_README_GENERATION) return help\n const binCommand = getBinCommand()\n if (binCommand === 'sanity') return help\n return help.replaceAll('$ sanity', `$ ${binCommand}`)\n}\n\n/**\n * Replace `sanity init` references in help text with the equivalent `create` command\n * for the detected package manager. Lines ending in just `sanity init\\n` (no flags)\n * are replaced without a flag separator, while lines with flags get the separator\n * (eg `--` for npm) so the flags are forwarded correctly.\n *\n * @internal\n */\nexport function replaceInitWithCreateCommand(help: string): string {\n const createCmd = guessCreateCommand()\n const flagSeparator = needsFlagSeparator() ? ' --' : ''\n\n // First replace all `sanity init` references that ends with a newline with the\n // create variant that does not include any flag separator (eg `--`). Then replace\n // the other references that do. Most package managers do not require the `--`\n // separator, but npm does. Only include it if we need to, as the commands look\n // cleaner without it.\n return help\n .replaceAll(/(\\s+)sanity\\s+init\\s*\\n/g, `$1${createCmd}\\n`)\n .replaceAll(/(\\s+)sanity(\\s+)init/g, `$1${createCmd}${flagSeparator}`)\n}\n\nfunction guessCreateCommand() {\n const pm = getRunningPackageManager()\n if (pm === 'yarn') return `yarn create sanity`\n if (pm === 'bun') return `bun create sanity@latest`\n if (pm === 'pnpm') return `pnpm create sanity@latest`\n return `npm create sanity@latest`\n}\n\nfunction needsFlagSeparator() {\n const pm = getRunningPackageManager()\n return pm === 'npm' || !pm\n}\n"],"names":["Help","getBinCommand","getRunningPackageManager","resolveTopicAliasInArgv","IS_README_GENERATION","process","argv","indexOf","includes","SanityHelp","formatCommand","command","help","isFromCreate","id","replaceInitWithCreateCommand","prefixBinName","formatRoot","formatTopic","topic","getCommandHelpClass","commandHelp","opts","flagSortOrder","showHelp","binCommand","replaceAll","createCmd","guessCreateCommand","flagSeparator","needsFlagSeparator","pm"],"mappings":"AAAA,SAA8BA,IAAI,QAAmB,cAAa;AAClE,SAAQC,aAAa,EAAEC,wBAAwB,QAAO,mCAAkC;AAExF,SAAQC,uBAAuB,QAAO,oBAAmB;AAEzD,qFAAqF;AACrF,uFAAuF;AACvF,qFAAqF;AACrF,4EAA4E;AAC5E,MAAMC,uBAAuB,AAACC,CAAAA,QAAQC,IAAI,CAACD,QAAQC,IAAI,CAACC,OAAO,CAAC,YAAY,EAAE,IAAI,EAAC,EAAGC,QAAQ,CAC5F;AAGF;;;;;;;CAOC,GACD,eAAe,MAAMC,mBAAmBT;IAC5BU,cAAcC,OAAyB,EAAU;QACzD,IAAIC,OAAO,KAAK,CAACF,cAAcC;QAE/B,wEAAwE;QACxE,iFAAiF;QACjF,yEAAyE;QACzE,MAAME,eAAeR,QAAQC,IAAI,CAACE,QAAQ,CAAC,oBAAoBG,QAAQG,EAAE,KAAK;QAC9E,IAAID,cAAc;YAChBD,OAAOG,6BAA6BH;QACtC;QAEA,OAAOI,cAAcJ;IACvB;IAEUK,aAAqB;QAC7B,OAAOD,cAAc,KAAK,CAACC;IAC7B;IAEUC,YAAYC,KAAuB,EAAU;QACrD,OAAOH,cAAc,KAAK,CAACE,YAAYC;IACzC;IAEmBC,oBAAoBT,OAAyB,EAAe;QAC7E,MAAMU,cAAc,KAAK,CAACD,oBAAoBT;QAC9C,IAAIA,QAAQG,EAAE,KAAK,SAAS;YAC1BO,YAAYC,IAAI,GAAG;gBAAC,GAAGD,YAAYC,IAAI;gBAAEC,eAAe;YAAM;QAChE;QACA,OAAOF;IACT;IAEA,MAAMG,SAASlB,IAAc,EAAiB;QAC5C,OAAO,KAAK,CAACkB,SAASrB,wBAAwBG;IAChD;AACF;AAEA;;CAEC,GACD,OAAO,SAASU,cAAcJ,IAAY;IACxC,IAAIR,sBAAsB,OAAOQ;IACjC,MAAMa,aAAaxB;IACnB,IAAIwB,eAAe,UAAU,OAAOb;IACpC,OAAOA,KAAKc,UAAU,CAAC,YAAY,CAAC,EAAE,EAAED,YAAY;AACtD;AAEA;;;;;;;CAOC,GACD,OAAO,SAASV,6BAA6BH,IAAY;IACvD,MAAMe,YAAYC;IAClB,MAAMC,gBAAgBC,uBAAuB,QAAQ;IAErD,+EAA+E;IAC/E,kFAAkF;IAClF,8EAA8E;IAC9E,+EAA+E;IAC/E,sBAAsB;IACtB,OAAOlB,KACJc,UAAU,CAAC,4BAA4B,CAAC,EAAE,EAAEC,UAAU,EAAE,CAAC,EACzDD,UAAU,CAAC,yBAAyB,CAAC,EAAE,EAAEC,YAAYE,eAAe;AACzE;AAEA,SAASD;IACP,MAAMG,KAAK7B;IACX,IAAI6B,OAAO,QAAQ,OAAO,CAAC,kBAAkB,CAAC;IAC9C,IAAIA,OAAO,OAAO,OAAO,CAAC,wBAAwB,CAAC;IACnD,IAAIA,OAAO,QAAQ,OAAO,CAAC,yBAAyB,CAAC;IACrD,OAAO,CAAC,wBAAwB,CAAC;AACnC;AAEA,SAASD;IACP,MAAMC,KAAK7B;IACX,OAAO6B,OAAO,SAAS,CAACA;AAC1B"}
@@ -0,0 +1,9 @@
1
+ export class AssetFileError extends Error {
2
+ reason;
3
+ constructor(reason){
4
+ super(reason), this.reason = reason;
5
+ this.name = 'AssetFileError';
6
+ }
7
+ }
8
+
9
+ //# sourceMappingURL=assetFileError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/assets/assetFileError.ts"],"sourcesContent":["export class AssetFileError extends Error {\n constructor(public readonly reason: 'not-file' | 'unreadable') {\n super(reason)\n this.name = 'AssetFileError'\n }\n}\n"],"names":["AssetFileError","Error","reason","name"],"mappings":"AAAA,OAAO,MAAMA,uBAAuBC;;IAClC,YAAY,AAAgBC,MAAiC,CAAE;QAC7D,KAAK,CAACA,cADoBA,SAAAA;QAE1B,IAAI,CAACC,IAAI,GAAG;IACd;AACF"}
@@ -0,0 +1,40 @@
1
+ import { open } from 'node:fs/promises';
2
+ import { uploadAsset } from '../../services/assets.js';
3
+ import { AssetFileError } from './assetFileError.js';
4
+ async function openAssetFile(filePath) {
5
+ let fileHandle;
6
+ try {
7
+ fileHandle = await open(filePath, 'r');
8
+ const fileStat = await fileHandle.stat();
9
+ if (!fileStat.isFile()) throw new AssetFileError('not-file');
10
+ return {
11
+ fileHandle,
12
+ fileSize: fileStat.size
13
+ };
14
+ } catch (error) {
15
+ await fileHandle?.close();
16
+ if (error instanceof AssetFileError) throw error;
17
+ throw new AssetFileError('unreadable');
18
+ }
19
+ }
20
+ export async function uploadAssetFromFile(options) {
21
+ options.signal?.throwIfAborted();
22
+ const { filePath, ...uploadOptions } = options;
23
+ const { fileHandle, fileSize } = await openAssetFile(filePath);
24
+ const fileStream = fileHandle.createReadStream({
25
+ autoClose: false
26
+ });
27
+ try {
28
+ options.signal?.throwIfAborted();
29
+ return await uploadAsset({
30
+ ...uploadOptions,
31
+ body: fileStream,
32
+ fileSize
33
+ });
34
+ } finally{
35
+ fileStream.destroy();
36
+ await fileHandle.close();
37
+ }
38
+ }
39
+
40
+ //# sourceMappingURL=uploadAssetFromFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/assets/uploadAssetFromFile.ts"],"sourcesContent":["import {type FileHandle, open} from 'node:fs/promises'\n\nimport {type AssetType, uploadAsset} from '../../services/assets.js'\nimport {AssetFileError} from './assetFileError.js'\n\ninterface UploadAssetFromFileOptions {\n assetType: AssetType\n dataset: string\n filename: string\n filePath: string\n projectId: string\n\n contentType?: string\n onProgress?: (percent: number) => void\n signal?: AbortSignal\n}\n\nasync function openAssetFile(\n filePath: string,\n): Promise<{fileHandle: FileHandle; fileSize: number}> {\n let fileHandle: FileHandle | undefined\n\n try {\n fileHandle = await open(filePath, 'r')\n const fileStat = await fileHandle.stat()\n if (!fileStat.isFile()) throw new AssetFileError('not-file')\n\n return {fileHandle, fileSize: fileStat.size}\n } catch (error) {\n await fileHandle?.close()\n if (error instanceof AssetFileError) throw error\n throw new AssetFileError('unreadable')\n }\n}\n\nexport async function uploadAssetFromFile(options: UploadAssetFromFileOptions) {\n options.signal?.throwIfAborted()\n const {filePath, ...uploadOptions} = options\n const {fileHandle, fileSize} = await openAssetFile(filePath)\n const fileStream = fileHandle.createReadStream({autoClose: false})\n\n try {\n options.signal?.throwIfAborted()\n return await uploadAsset({...uploadOptions, body: fileStream, fileSize})\n } finally {\n fileStream.destroy()\n await fileHandle.close()\n }\n}\n"],"names":["open","uploadAsset","AssetFileError","openAssetFile","filePath","fileHandle","fileStat","stat","isFile","fileSize","size","error","close","uploadAssetFromFile","options","signal","throwIfAborted","uploadOptions","fileStream","createReadStream","autoClose","body","destroy"],"mappings":"AAAA,SAAyBA,IAAI,QAAO,mBAAkB;AAEtD,SAAwBC,WAAW,QAAO,2BAA0B;AACpE,SAAQC,cAAc,QAAO,sBAAqB;AAclD,eAAeC,cACbC,QAAgB;IAEhB,IAAIC;IAEJ,IAAI;QACFA,aAAa,MAAML,KAAKI,UAAU;QAClC,MAAME,WAAW,MAAMD,WAAWE,IAAI;QACtC,IAAI,CAACD,SAASE,MAAM,IAAI,MAAM,IAAIN,eAAe;QAEjD,OAAO;YAACG;YAAYI,UAAUH,SAASI,IAAI;QAAA;IAC7C,EAAE,OAAOC,OAAO;QACd,MAAMN,YAAYO;QAClB,IAAID,iBAAiBT,gBAAgB,MAAMS;QAC3C,MAAM,IAAIT,eAAe;IAC3B;AACF;AAEA,OAAO,eAAeW,oBAAoBC,OAAmC;IAC3EA,QAAQC,MAAM,EAAEC;IAChB,MAAM,EAACZ,QAAQ,EAAE,GAAGa,eAAc,GAAGH;IACrC,MAAM,EAACT,UAAU,EAAEI,QAAQ,EAAC,GAAG,MAAMN,cAAcC;IACnD,MAAMc,aAAab,WAAWc,gBAAgB,CAAC;QAACC,WAAW;IAAK;IAEhE,IAAI;QACFN,QAAQC,MAAM,EAAEC;QAChB,OAAO,MAAMf,YAAY;YAAC,GAAGgB,aAAa;YAAEI,MAAMH;YAAYT;QAAQ;IACxE,SAAU;QACRS,WAAWI,OAAO;QAClB,MAAMjB,WAAWO,KAAK;IACxB;AACF"}
@@ -0,0 +1,76 @@
1
+ import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
2
+ import { spinner } from '@sanity/cli-core/ux';
3
+ import { uploadAssetFromFile } from './uploadAssetFromFile.js';
4
+ export async function uploadAssetWithProgress(options) {
5
+ const { assetType, isInteractive, logToStderr, ...uploadOptions } = options;
6
+ const uploadMessage = `Uploading ${assetType} asset`;
7
+ const assetDocumentMessage = `Creating ${assetType} asset document`;
8
+ const executionContext = getCliExecutionContext();
9
+ const isTTY = !executionContext && isInteractive;
10
+ const uploadProgress = spinner(isTTY ? `${uploadMessage} [0%]. Large uploads may take several minutes.` : `${uploadMessage}. Large uploads may take several minutes.`).start();
11
+ if (executionContext) logToStderr(`${uploadMessage}. Large uploads may take several minutes.`);
12
+ let assetDocumentProgress;
13
+ let assetDocumentStarted = false;
14
+ let lastReportedProgress = 0;
15
+ let nextNonInteractiveCheckpoint = 25;
16
+ const startAssetDocument = ()=>{
17
+ if (assetDocumentStarted) return;
18
+ assetDocumentStarted = true;
19
+ if (!isTTY) {
20
+ uploadProgress.text = assetDocumentMessage;
21
+ logToStderr(assetDocumentMessage);
22
+ return;
23
+ }
24
+ uploadProgress.succeed(`${uploadMessage} [100%]`);
25
+ assetDocumentProgress = spinner(assetDocumentMessage).start();
26
+ };
27
+ const uploadController = new AbortController();
28
+ const interruptUpload = ()=>uploadController.abort(new Error('SIGINT'));
29
+ const handlesInterrupt = !executionContext;
30
+ if (handlesInterrupt) process.once('SIGINT', interruptUpload);
31
+ // Ora uses raw mode for Ctrl+C, but its input listener does not always make stdin flow.
32
+ const resumesInterruptInput = handlesInterrupt && isTTY && uploadProgress.isSpinning && process.stdin.isTTY && process.stdin.isRaw && process.stdin.readableFlowing !== true;
33
+ if (resumesInterruptInput) process.stdin.resume();
34
+ try {
35
+ const asset = await uploadAssetFromFile({
36
+ ...uploadOptions,
37
+ assetType,
38
+ onProgress: (percent)=>{
39
+ const progress = Math.min(100, Math.floor(percent));
40
+ if (progress <= lastReportedProgress) return;
41
+ lastReportedProgress = progress;
42
+ if (progress === 100) {
43
+ startAssetDocument();
44
+ } else {
45
+ uploadProgress.text = `${uploadMessage} [${progress}%]`;
46
+ if (!isTTY && progress >= nextNonInteractiveCheckpoint) {
47
+ const checkpoint = Math.min(75, Math.floor(progress / 25) * 25);
48
+ logToStderr(`${uploadMessage} [${checkpoint}%]`);
49
+ nextNonInteractiveCheckpoint = checkpoint + 25;
50
+ }
51
+ }
52
+ },
53
+ signal: uploadController.signal
54
+ });
55
+ if (isTTY) {
56
+ startAssetDocument();
57
+ assetDocumentProgress?.succeed(assetDocumentMessage);
58
+ spinner().succeed(`Uploaded ${assetType} asset: ${asset._id}`);
59
+ } else if (executionContext) {
60
+ logToStderr(`Uploaded ${assetType} asset: ${asset._id}`);
61
+ } else {
62
+ uploadProgress.succeed(`Uploaded ${assetType} asset: ${asset._id}`);
63
+ }
64
+ return asset;
65
+ } catch (error) {
66
+ const activeProgress = assetDocumentProgress ?? uploadProgress;
67
+ activeProgress.stop();
68
+ uploadController.signal.throwIfAborted();
69
+ throw error;
70
+ } finally{
71
+ if (handlesInterrupt) process.off('SIGINT', interruptUpload);
72
+ if (resumesInterruptInput) process.stdin.pause();
73
+ }
74
+ }
75
+
76
+ //# sourceMappingURL=uploadAssetWithProgress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/assets/uploadAssetWithProgress.ts"],"sourcesContent":["import {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {type AssetType} from '../../services/assets.js'\nimport {uploadAssetFromFile} from './uploadAssetFromFile.js'\n\ninterface UploadAssetWithProgressOptions {\n assetType: AssetType\n dataset: string\n filename: string\n filePath: string\n isInteractive: boolean\n logToStderr: (message: string) => void\n projectId: string\n\n contentType?: string\n}\n\nexport async function uploadAssetWithProgress(options: UploadAssetWithProgressOptions) {\n const {assetType, isInteractive, logToStderr, ...uploadOptions} = options\n const uploadMessage = `Uploading ${assetType} asset`\n const assetDocumentMessage = `Creating ${assetType} asset document`\n const executionContext = getCliExecutionContext()\n const isTTY = !executionContext && isInteractive\n const uploadProgress = spinner(\n isTTY\n ? `${uploadMessage} [0%]. Large uploads may take several minutes.`\n : `${uploadMessage}. Large uploads may take several minutes.`,\n ).start()\n if (executionContext) logToStderr(`${uploadMessage}. Large uploads may take several minutes.`)\n\n let assetDocumentProgress: ReturnType<typeof spinner> | undefined\n let assetDocumentStarted = false\n let lastReportedProgress = 0\n let nextNonInteractiveCheckpoint = 25\n\n const startAssetDocument = () => {\n if (assetDocumentStarted) return\n assetDocumentStarted = true\n if (!isTTY) {\n uploadProgress.text = assetDocumentMessage\n logToStderr(assetDocumentMessage)\n return\n }\n\n uploadProgress.succeed(`${uploadMessage} [100%]`)\n assetDocumentProgress = spinner(assetDocumentMessage).start()\n }\n\n const uploadController = new AbortController()\n const interruptUpload = () => uploadController.abort(new Error('SIGINT'))\n const handlesInterrupt = !executionContext\n if (handlesInterrupt) process.once('SIGINT', interruptUpload)\n\n // Ora uses raw mode for Ctrl+C, but its input listener does not always make stdin flow.\n const resumesInterruptInput =\n handlesInterrupt &&\n isTTY &&\n uploadProgress.isSpinning &&\n process.stdin.isTTY &&\n process.stdin.isRaw &&\n process.stdin.readableFlowing !== true\n if (resumesInterruptInput) process.stdin.resume()\n\n try {\n const asset = await uploadAssetFromFile({\n ...uploadOptions,\n assetType,\n onProgress: (percent) => {\n const progress = Math.min(100, Math.floor(percent))\n if (progress <= lastReportedProgress) return\n\n lastReportedProgress = progress\n if (progress === 100) {\n startAssetDocument()\n } else {\n uploadProgress.text = `${uploadMessage} [${progress}%]`\n if (!isTTY && progress >= nextNonInteractiveCheckpoint) {\n const checkpoint = Math.min(75, Math.floor(progress / 25) * 25)\n logToStderr(`${uploadMessage} [${checkpoint}%]`)\n nextNonInteractiveCheckpoint = checkpoint + 25\n }\n }\n },\n signal: uploadController.signal,\n })\n\n if (isTTY) {\n startAssetDocument()\n assetDocumentProgress?.succeed(assetDocumentMessage)\n spinner().succeed(`Uploaded ${assetType} asset: ${asset._id}`)\n } else if (executionContext) {\n logToStderr(`Uploaded ${assetType} asset: ${asset._id}`)\n } else {\n uploadProgress.succeed(`Uploaded ${assetType} asset: ${asset._id}`)\n }\n\n return asset\n } catch (error) {\n const activeProgress = assetDocumentProgress ?? uploadProgress\n activeProgress.stop()\n uploadController.signal.throwIfAborted()\n throw error\n } finally {\n if (handlesInterrupt) process.off('SIGINT', interruptUpload)\n if (resumesInterruptInput) process.stdin.pause()\n }\n}\n"],"names":["getCliExecutionContext","spinner","uploadAssetFromFile","uploadAssetWithProgress","options","assetType","isInteractive","logToStderr","uploadOptions","uploadMessage","assetDocumentMessage","executionContext","isTTY","uploadProgress","start","assetDocumentProgress","assetDocumentStarted","lastReportedProgress","nextNonInteractiveCheckpoint","startAssetDocument","text","succeed","uploadController","AbortController","interruptUpload","abort","Error","handlesInterrupt","process","once","resumesInterruptInput","isSpinning","stdin","isRaw","readableFlowing","resume","asset","onProgress","percent","progress","Math","min","floor","checkpoint","signal","_id","error","activeProgress","stop","throwIfAborted","off","pause"],"mappings":"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"}
@@ -5,7 +5,7 @@ import { exitCodes } from '@sanity/cli-core';
5
5
  import { getErrorMessage } from '@sanity/cli-core/errors';
6
6
  import { getCoreAppUrl } from '@sanity/cli-core/util';
7
7
  import { spinner } from '@sanity/cli-core/ux';
8
- import { createCoreApp, deployConfig, deployWorkbenchApp, getApplicationUrl, getWorkbench, resolveInstallationId, summarizeConfig } from '@sanity/workbench-cli/deploy';
8
+ import { createCoreApp, deployConfig, deployWorkbenchApp, getApplicationUrl, getWorkbench, resolveInstallationId, summarizeConfig, toWorkbenchPayload } from '@sanity/workbench-cli/deploy';
9
9
  import { pack } from 'tar-fs';
10
10
  import { createDeployment, updateUserApplication } from '../../services/userApplications.js';
11
11
  import { getAppId } from '../../util/appId.js';
@@ -15,7 +15,7 @@ import { extractCoreAppManifest, readIconFromPath, resolveTitleUpdate } from '..
15
15
  import { createUserApplication } from './createUserApplication.js';
16
16
  import { checkAppId, checkAppTarget, checkAutoUpdates, checkBuild, checkPackageVersion, verifyOutputDir } from './deployChecks.js';
17
17
  import { deployDebug } from './deployDebug.js';
18
- import { listDeploymentFiles, reportExposes } from './deploymentPlan.js';
18
+ import { listDeploymentFiles, reportInterfaces } from './deploymentPlan.js';
19
19
  import { runDeploy } from './deployRunner.js';
20
20
  import { findUserApplication } from './findUserApplication.js';
21
21
  const APP_PACKAGE = '@sanity/sdk-react';
@@ -83,6 +83,7 @@ export function deployApp(options) {
83
83
  });
84
84
  let application = null;
85
85
  let appCreated = false;
86
+ let workbenchApp;
86
87
  if (flags.external) {
87
88
  reporter.report({
88
89
  message: EXTERNAL_APP_NOT_SUPPORTED,
@@ -90,13 +91,13 @@ export function deployApp(options) {
90
91
  status: 'fail'
91
92
  });
92
93
  } else if (deployApplication && workbench) {
93
- await checkAppTarget(reporter, {
94
+ workbenchApp = (await checkAppTarget(reporter, {
94
95
  appId,
95
96
  isWorkbenchApp: true,
96
97
  organizationId,
97
98
  slug: workbench.slug,
98
99
  title: appTitle
99
- });
100
+ }))?.application;
100
101
  } else if (deployApplication) {
101
102
  ;
102
103
  ({ application, created: appCreated } = await resolveAppApplication(options, {
@@ -122,14 +123,16 @@ export function deployApp(options) {
122
123
  let applicationCreated = false;
123
124
  let rollbackApp;
124
125
  if (!dryRun && deployApplication && workbench && organizationId && !applicationId) {
125
- ;
126
- ({ applicationId, rollback: rollbackApp } = await createCoreApp({
126
+ const created = await createCoreApp({
127
127
  isSingleton: workbench.isSingleton,
128
128
  organizationId,
129
129
  slug: workbench.slug,
130
130
  title: appTitle,
131
131
  visibility: workbench.visibility
132
- }));
132
+ });
133
+ workbenchApp = created.application;
134
+ applicationId = created.application.id;
135
+ rollbackApp = created.rollback;
133
136
  applicationCreated = true;
134
137
  }
135
138
  // A record created above is stranded at its slug (and blocks retries) if any
@@ -183,7 +186,6 @@ export function deployApp(options) {
183
186
  });
184
187
  config = summarizeConfig(workbench.config);
185
188
  reporter.report(installationId ? {
186
- config,
187
189
  message: config,
188
190
  status: 'pass'
189
191
  } : {
@@ -193,12 +195,10 @@ export function deployApp(options) {
193
195
  status: 'fail'
194
196
  });
195
197
  }
196
- // Report the exposes deploying with the application, both modes.
197
- const exposes = deployApplication && workbench ? reportExposes(reporter, workbench) : [];
198
+ const interfaces = deployApplication && workbench ? reportInterfaces(reporter, workbench) : null;
198
199
  // Surface the app's explicit singleton flag when set, both modes.
199
200
  if (deployApplication && workbench?.isSingleton !== undefined) {
200
201
  reporter.report({
201
- isSingleton: workbench.isSingleton,
202
202
  message: `Singleton: ${workbench.isSingleton}`,
203
203
  status: 'pass'
204
204
  });
@@ -217,20 +217,33 @@ export function deployApp(options) {
217
217
  });
218
218
  }
219
219
  };
220
+ const payload = {
221
+ appId: appId ?? null,
222
+ isAutoUpdating,
223
+ ...organizationId ? {
224
+ organizationId
225
+ } : {},
226
+ type: 'coreApp',
227
+ version,
228
+ ...toWorkbenchPayload(workbench, {
229
+ config,
230
+ interfaces,
231
+ title: appTitle
232
+ })
233
+ };
220
234
  // Dry run stops here — everything below mutates.
221
- if (dryRun) return;
235
+ if (dryRun) return {
236
+ application: null,
237
+ payload
238
+ };
222
239
  // A config-only singleton ships no application, only its config.
223
240
  if (!deployApplication) {
224
241
  await deployApplicationConfig();
225
242
  if (installationId && version) {
226
243
  return {
227
- applicationType: 'coreApp',
228
- applicationVersion: version,
229
- ...config ? {
230
- config
231
- } : {},
244
+ application: null,
232
245
  installationId,
233
- target: null
246
+ payload
234
247
  };
235
248
  }
236
249
  return;
@@ -272,24 +285,10 @@ export function deployApp(options) {
272
285
  url
273
286
  });
274
287
  return {
275
- applicationType: 'coreApp',
276
- applicationVersion: version,
277
- ...exposes.length > 0 ? {
278
- exposes
279
- } : {},
280
- ...workbench.isSingleton === undefined ? {} : {
281
- isSingleton: workbench.isSingleton
282
- },
283
- target: {
284
- action: applicationCreated ? 'create' : 'update',
285
- applicationId,
286
- // A redeploy targets an existing app; only a create reports the slug.
287
- ...applicationCreated ? {
288
- slug: workbench.slug
289
- } : {},
290
- title: appTitle,
291
- url
292
- }
288
+ action: applicationCreated ? 'create' : 'update',
289
+ application: workbenchApp ?? null,
290
+ payload,
291
+ url
293
292
  };
294
293
  }
295
294
  // A real deploy has already exited if anything failed; landing here without a
@@ -317,14 +316,10 @@ export function deployApp(options) {
317
316
  title: application.title
318
317
  });
319
318
  return {
320
- applicationType: 'coreApp',
321
- applicationVersion: version,
322
- target: {
323
- action: appCreated ? 'create' : 'update',
324
- applicationId: application.id,
325
- title: application.title ?? null,
326
- url: getCoreAppUrl(application.organizationId, application.id)
327
- }
319
+ action: appCreated ? 'create' : 'update',
320
+ application,
321
+ payload,
322
+ url: getCoreAppUrl(application.organizationId, application.id)
328
323
  };
329
324
  } catch (err) {
330
325
  await rollbackApp?.();