@sanity/cli 7.18.0 → 8.0.1

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 (135) hide show
  1. package/README.md +74 -4
  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/init/sdkAppDependencies.js +1 -1
  29. package/dist/actions/init/sdkAppDependencies.js.map +1 -1
  30. package/dist/actions/init/studioDependencies.js +1 -1
  31. package/dist/actions/init/studioDependencies.js.map +1 -1
  32. package/dist/actions/schema/listSchemas.js +1 -1
  33. package/dist/actions/schema/listSchemas.js.map +1 -1
  34. package/dist/actions/tokens/constants.js +2 -2
  35. package/dist/actions/tokens/constants.js.map +1 -1
  36. package/dist/actions/tokens/expiry.js +35 -0
  37. package/dist/actions/tokens/expiry.js.map +1 -0
  38. package/dist/actions/tokens/types.js.map +1 -1
  39. package/dist/actions/tokens/validateRole.js +5 -2
  40. package/dist/actions/tokens/validateRole.js.map +1 -1
  41. package/dist/actions/typegen/count.js +6 -0
  42. package/dist/actions/typegen/count.js.map +1 -0
  43. package/dist/actions/typegen/formatPath.js +5 -0
  44. package/dist/actions/typegen/formatPath.js.map +1 -0
  45. package/dist/actions/typegen/percent.js +8 -0
  46. package/dist/actions/typegen/percent.js.map +1 -0
  47. package/dist/actions/typegen/renderTypegenProgress.js +88 -0
  48. package/dist/actions/typegen/renderTypegenProgress.js.map +1 -0
  49. package/dist/actions/undeploy/adapters.js +6 -9
  50. package/dist/actions/undeploy/adapters.js.map +1 -1
  51. package/dist/actions/undeploy/runUndeploy.js +12 -4
  52. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  53. package/dist/actions/undeploy/undeployPlan.js +6 -14
  54. package/dist/actions/undeploy/undeployPlan.js.map +1 -1
  55. package/dist/commands/assets/upload.js +160 -0
  56. package/dist/commands/assets/upload.js.map +1 -0
  57. package/dist/commands/backups/list.js +1 -1
  58. package/dist/commands/backups/list.js.map +1 -1
  59. package/dist/commands/datasets/copy.js +1 -1
  60. package/dist/commands/datasets/copy.js.map +1 -1
  61. package/dist/commands/login.js +2 -8
  62. package/dist/commands/login.js.map +1 -1
  63. package/dist/commands/mcp/configure.js.map +1 -1
  64. package/dist/commands/migrations/list.js +1 -1
  65. package/dist/commands/migrations/list.js.map +1 -1
  66. package/dist/commands/migrations/run.js +67 -3
  67. package/dist/commands/migrations/run.js.map +1 -1
  68. package/dist/commands/new.js +4 -3
  69. package/dist/commands/new.js.map +1 -1
  70. package/dist/commands/organizations/list.js +1 -1
  71. package/dist/commands/organizations/list.js.map +1 -1
  72. package/dist/commands/projects/list.js +39 -12
  73. package/dist/commands/projects/list.js.map +1 -1
  74. package/dist/commands/projects/unclaimed.js +39 -33
  75. package/dist/commands/projects/unclaimed.js.map +1 -1
  76. package/dist/commands/tokens/create.js +92 -7
  77. package/dist/commands/tokens/create.js.map +1 -1
  78. package/dist/commands/tokens/delete.js +2 -2
  79. package/dist/commands/tokens/delete.js.map +1 -1
  80. package/dist/commands/tokens/list.js +12 -11
  81. package/dist/commands/tokens/list.js.map +1 -1
  82. package/dist/commands/tokens/rotate.js +95 -0
  83. package/dist/commands/tokens/rotate.js.map +1 -0
  84. package/dist/commands/typegen/generate.js +222 -0
  85. package/dist/commands/typegen/generate.js.map +1 -0
  86. package/dist/commands/users/list.js +8 -8
  87. package/dist/commands/users/list.js.map +1 -1
  88. package/dist/exports/_internal.d.ts +29 -0
  89. package/dist/exports/_internal.js +1 -0
  90. package/dist/exports/_internal.js.map +1 -1
  91. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +6 -0
  92. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  93. package/dist/exports/invokeSanityCli/help.js +4 -0
  94. package/dist/exports/invokeSanityCli/help.js.map +1 -1
  95. package/dist/exports/invokeSanityCli/index.js +2 -0
  96. package/dist/exports/invokeSanityCli/index.js.map +1 -1
  97. package/dist/hooks/commandNotFound/topicAliases.js +13 -40
  98. package/dist/hooks/commandNotFound/topicAliases.js.map +1 -1
  99. package/dist/hooks/prerun/setupTelemetry.js +3 -2
  100. package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
  101. package/dist/server/vite/plugin-typegen.js +4 -16
  102. package/dist/server/vite/plugin-typegen.js.map +1 -1
  103. package/dist/services/assets.js +46 -0
  104. package/dist/services/assets.js.map +1 -0
  105. package/dist/services/tokens.js +81 -31
  106. package/dist/services/tokens.js.map +1 -1
  107. package/dist/topicAliases.js +43 -0
  108. package/dist/topicAliases.js.map +1 -1
  109. package/dist/util/parseArguments.js +7 -2
  110. package/dist/util/parseArguments.js.map +1 -1
  111. package/dist/util/readTokenFromStdin.js +17 -0
  112. package/dist/util/readTokenFromStdin.js.map +1 -0
  113. package/dist/util/responsiveTable.js +97 -0
  114. package/dist/util/responsiveTable.js.map +1 -0
  115. package/dist/util/telemetry/commandTelemetry.js +34 -0
  116. package/dist/util/telemetry/commandTelemetry.js.map +1 -0
  117. package/dist/util/unclaimedProjects.js +10 -0
  118. package/dist/util/unclaimedProjects.js.map +1 -1
  119. package/oclif.config.js +2 -4
  120. package/oclif.manifest.json +688 -476
  121. package/package.json +12 -10
  122. package/templates/shopify/components/inputs/ShopifyMetafield.tsx +20 -0
  123. package/templates/shopify/docs/features.md +24 -0
  124. package/templates/shopify/schemaTypes/index.ts +2 -0
  125. package/templates/shopify/schemaTypes/objects/shopify/shopifyCollectionType.ts +18 -1
  126. package/templates/shopify/schemaTypes/objects/shopify/shopifyMetafieldType.ts +47 -0
  127. package/templates/shopify/schemaTypes/objects/shopify/shopifyProductType.ts +17 -0
  128. package/templates/shopify/utils/formatMetafieldValue.ts +30 -0
  129. package/templates/shopify-online-storefront/components/inputs/ShopifyMetafield.tsx +22 -0
  130. package/templates/shopify-online-storefront/schemaTypes/index.ts +4 -0
  131. package/templates/shopify-online-storefront/schemaTypes/objects/shop.ts +16 -0
  132. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyCollection.ts +19 -0
  133. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyMetafield.ts +53 -0
  134. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyProduct.ts +19 -0
  135. 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
@@ -3601,15 +3637,16 @@ Create a new API token for the project
3601
3637
 
3602
3638
  ```
3603
3639
  USAGE
3604
- $ 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]
3605
3641
 
3606
3642
  ARGUMENTS
3607
3643
  [LABEL] Label for the new token
3608
3644
 
3609
3645
  FLAGS
3610
- -y, --yes Skip prompts and use defaults (unattended mode)
3611
- --json Output as JSON
3612
- --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)
3613
3650
 
3614
3651
  OVERRIDE FLAGS
3615
3652
  -p, --project-id=<id> Project ID to create token in (overrides CLI configuration)
@@ -3630,6 +3667,10 @@ EXAMPLES
3630
3667
 
3631
3668
  $ sanity tokens create "CI Token" --role=editor --yes
3632
3669
 
3670
+ Create a token that expires on a given date
3671
+
3672
+ $ sanity tokens create "Build Token" --expires-at 2027-01-01
3673
+
3633
3674
  Output token information as JSON
3634
3675
 
3635
3676
  $ sanity tokens create "API Token" --json
@@ -3708,6 +3749,35 @@ EXAMPLES
3708
3749
  $ sanity tokens list --project-id abc123
3709
3750
  ```
3710
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
+
3711
3781
  ## `sanity typegen generate`
3712
3782
 
3713
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":"AAAA,SAAQA,sBAAsB,QAAO,oCAAmC;AACxE,SAAQC,OAAO,QAAO,sBAAqB;AAG3C,SAAQC,mBAAmB,QAAO,2BAA0B;AAc5D,OAAO,eAAeC,wBAAwBC,OAAuC;IACnF,MAAM,EAACC,SAAS,EAAEC,aAAa,EAAEC,WAAW,EAAE,GAAGC,eAAc,GAAGJ;IAClE,MAAMK,gBAAgB,CAAC,UAAU,EAAEJ,UAAU,MAAM,CAAC;IACpD,MAAMK,uBAAuB,CAAC,SAAS,EAAEL,UAAU,eAAe,CAAC;IACnE,MAAMM,mBAAmBX;IACzB,MAAMY,QAAQ,CAACD,oBAAoBL;IACnC,MAAMO,iBAAiBZ,QACrBW,QACI,GAAGH,cAAc,8CAA8C,CAAC,GAChE,GAAGA,cAAc,yCAAyC,CAAC,EAC/DK,KAAK;IACP,IAAIH,kBAAkBJ,YAAY,GAAGE,cAAc,yCAAyC,CAAC;IAE7F,IAAIM;IACJ,IAAIC,uBAAuB;IAC3B,IAAIC,uBAAuB;IAC3B,IAAIC,+BAA+B;IAEnC,MAAMC,qBAAqB;QACzB,IAAIH,sBAAsB;QAC1BA,uBAAuB;QACvB,IAAI,CAACJ,OAAO;YACVC,eAAeO,IAAI,GAAGV;YACtBH,YAAYG;YACZ;QACF;QAEAG,eAAeQ,OAAO,CAAC,GAAGZ,cAAc,OAAO,CAAC;QAChDM,wBAAwBd,QAAQS,sBAAsBI,KAAK;IAC7D;IAEA,MAAMQ,mBAAmB,IAAIC;IAC7B,MAAMC,kBAAkB,IAAMF,iBAAiBG,KAAK,CAAC,IAAIC,MAAM;IAC/D,MAAMC,mBAAmB,CAAChB;IAC1B,IAAIgB,kBAAkBC,QAAQC,IAAI,CAAC,UAAUL;IAE7C,wFAAwF;IACxF,MAAMM,wBACJH,oBACAf,SACAC,eAAekB,UAAU,IACzBH,QAAQI,KAAK,CAACpB,KAAK,IACnBgB,QAAQI,KAAK,CAACC,KAAK,IACnBL,QAAQI,KAAK,CAACE,eAAe,KAAK;IACpC,IAAIJ,uBAAuBF,QAAQI,KAAK,CAACG,MAAM;IAE/C,IAAI;QACF,MAAMC,QAAQ,MAAMlC,oBAAoB;YACtC,GAAGM,aAAa;YAChBH;YACAgC,YAAY,CAACC;gBACX,MAAMC,WAAWC,KAAKC,GAAG,CAAC,KAAKD,KAAKE,KAAK,CAACJ;gBAC1C,IAAIC,YAAYtB,sBAAsB;gBAEtCA,uBAAuBsB;gBACvB,IAAIA,aAAa,KAAK;oBACpBpB;gBACF,OAAO;oBACLN,eAAeO,IAAI,GAAG,GAAGX,cAAc,EAAE,EAAE8B,SAAS,EAAE,CAAC;oBACvD,IAAI,CAAC3B,SAAS2B,YAAYrB,8BAA8B;wBACtD,MAAMyB,aAAaH,KAAKC,GAAG,CAAC,IAAID,KAAKE,KAAK,CAACH,WAAW,MAAM;wBAC5DhC,YAAY,GAAGE,cAAc,EAAE,EAAEkC,WAAW,EAAE,CAAC;wBAC/CzB,+BAA+ByB,aAAa;oBAC9C;gBACF;YACF;YACAC,QAAQtB,iBAAiBsB,MAAM;QACjC;QAEA,IAAIhC,OAAO;YACTO;YACAJ,uBAAuBM,QAAQX;YAC/BT,UAAUoB,OAAO,CAAC,CAAC,SAAS,EAAEhB,UAAU,QAAQ,EAAE+B,MAAMS,GAAG,EAAE;QAC/D,OAAO,IAAIlC,kBAAkB;YAC3BJ,YAAY,CAAC,SAAS,EAAEF,UAAU,QAAQ,EAAE+B,MAAMS,GAAG,EAAE;QACzD,OAAO;YACLhC,eAAeQ,OAAO,CAAC,CAAC,SAAS,EAAEhB,UAAU,QAAQ,EAAE+B,MAAMS,GAAG,EAAE;QACpE;QAEA,OAAOT;IACT,EAAE,OAAOU,OAAO;QACd,MAAMC,iBAAiBhC,yBAAyBF;QAChDkC,eAAeC,IAAI;QACnB1B,iBAAiBsB,MAAM,CAACK,cAAc;QACtC,MAAMH;IACR,SAAU;QACR,IAAInB,kBAAkBC,QAAQsB,GAAG,CAAC,UAAU1B;QAC5C,IAAIM,uBAAuBF,QAAQI,KAAK,CAACmB,KAAK;IAChD;AACF"}
@@ -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?.();