@sanity/cli 7.18.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.
- package/README.md +74 -4
- package/dist/SanityHelp.js +1 -35
- package/dist/SanityHelp.js.map +1 -1
- package/dist/actions/assets/assetFileError.js +9 -0
- package/dist/actions/assets/assetFileError.js.map +1 -0
- package/dist/actions/assets/uploadAssetFromFile.js +40 -0
- package/dist/actions/assets/uploadAssetFromFile.js.map +1 -0
- package/dist/actions/assets/uploadAssetWithProgress.js +76 -0
- package/dist/actions/assets/uploadAssetWithProgress.js.map +1 -0
- package/dist/actions/deploy/deployApp.js +39 -44
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployChecks.js +21 -13
- package/dist/actions/deploy/deployChecks.js.map +1 -1
- package/dist/actions/deploy/deployRunner.js +8 -7
- package/dist/actions/deploy/deployRunner.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +55 -35
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/deploymentPlan.js +17 -29
- package/dist/actions/deploy/deploymentPlan.js.map +1 -1
- package/dist/actions/deploy/resolveDeployTarget.js +2 -8
- package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
- package/dist/actions/dev/servers/getDevServerConfig.js +2 -8
- package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
- package/dist/actions/dev/servers/startStudioDevServer.js +7 -1
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
- package/dist/actions/init/bootstrapRemoteTemplate.js +2 -2
- package/dist/actions/init/bootstrapRemoteTemplate.js.map +1 -1
- package/dist/actions/schema/listSchemas.js +1 -1
- package/dist/actions/schema/listSchemas.js.map +1 -1
- package/dist/actions/tokens/constants.js +2 -2
- package/dist/actions/tokens/constants.js.map +1 -1
- package/dist/actions/tokens/expiry.js +35 -0
- package/dist/actions/tokens/expiry.js.map +1 -0
- package/dist/actions/tokens/types.js.map +1 -1
- package/dist/actions/tokens/validateRole.js +5 -2
- package/dist/actions/tokens/validateRole.js.map +1 -1
- package/dist/actions/typegen/count.js +6 -0
- package/dist/actions/typegen/count.js.map +1 -0
- package/dist/actions/typegen/formatPath.js +5 -0
- package/dist/actions/typegen/formatPath.js.map +1 -0
- package/dist/actions/typegen/percent.js +8 -0
- package/dist/actions/typegen/percent.js.map +1 -0
- package/dist/actions/typegen/renderTypegenProgress.js +88 -0
- package/dist/actions/typegen/renderTypegenProgress.js.map +1 -0
- package/dist/actions/undeploy/adapters.js +6 -9
- package/dist/actions/undeploy/adapters.js.map +1 -1
- package/dist/actions/undeploy/runUndeploy.js +12 -4
- package/dist/actions/undeploy/runUndeploy.js.map +1 -1
- package/dist/actions/undeploy/undeployPlan.js +6 -14
- package/dist/actions/undeploy/undeployPlan.js.map +1 -1
- package/dist/commands/assets/upload.js +126 -0
- package/dist/commands/assets/upload.js.map +1 -0
- package/dist/commands/backups/list.js +1 -1
- package/dist/commands/backups/list.js.map +1 -1
- package/dist/commands/datasets/copy.js +1 -1
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/login.js +2 -8
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/migrations/list.js +1 -1
- package/dist/commands/migrations/list.js.map +1 -1
- package/dist/commands/migrations/run.js +67 -3
- package/dist/commands/migrations/run.js.map +1 -1
- package/dist/commands/new.js +4 -3
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/organizations/list.js +1 -1
- package/dist/commands/organizations/list.js.map +1 -1
- package/dist/commands/projects/list.js +39 -12
- package/dist/commands/projects/list.js.map +1 -1
- package/dist/commands/projects/unclaimed.js +39 -33
- package/dist/commands/projects/unclaimed.js.map +1 -1
- package/dist/commands/tokens/create.js +92 -7
- package/dist/commands/tokens/create.js.map +1 -1
- package/dist/commands/tokens/delete.js +2 -2
- package/dist/commands/tokens/delete.js.map +1 -1
- package/dist/commands/tokens/list.js +12 -11
- package/dist/commands/tokens/list.js.map +1 -1
- package/dist/commands/tokens/rotate.js +95 -0
- package/dist/commands/tokens/rotate.js.map +1 -0
- package/dist/commands/typegen/generate.js +222 -0
- package/dist/commands/typegen/generate.js.map +1 -0
- package/dist/commands/users/list.js +8 -8
- package/dist/commands/users/list.js.map +1 -1
- package/dist/exports/_internal.d.ts +29 -0
- package/dist/exports/_internal.js +1 -0
- package/dist/exports/_internal.js.map +1 -1
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +6 -0
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
- package/dist/exports/invokeSanityCli/help.js +4 -0
- package/dist/exports/invokeSanityCli/help.js.map +1 -1
- package/dist/exports/invokeSanityCli/index.js +2 -0
- package/dist/exports/invokeSanityCli/index.js.map +1 -1
- package/dist/hooks/commandNotFound/topicAliases.js +13 -40
- package/dist/hooks/commandNotFound/topicAliases.js.map +1 -1
- package/dist/hooks/prerun/setupTelemetry.js +3 -2
- package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
- package/dist/server/vite/plugin-typegen.js +4 -16
- package/dist/server/vite/plugin-typegen.js.map +1 -1
- package/dist/services/assets.js +46 -0
- package/dist/services/assets.js.map +1 -0
- package/dist/services/tokens.js +81 -31
- package/dist/services/tokens.js.map +1 -1
- package/dist/topicAliases.js +43 -0
- package/dist/topicAliases.js.map +1 -1
- package/dist/util/parseArguments.js +7 -2
- package/dist/util/parseArguments.js.map +1 -1
- package/dist/util/readTokenFromStdin.js +17 -0
- package/dist/util/readTokenFromStdin.js.map +1 -0
- package/dist/util/responsiveTable.js +97 -0
- package/dist/util/responsiveTable.js.map +1 -0
- package/dist/util/telemetry/commandTelemetry.js +34 -0
- package/dist/util/telemetry/commandTelemetry.js.map +1 -0
- package/dist/util/unclaimedProjects.js +10 -0
- package/dist/util/unclaimedProjects.js.map +1 -1
- package/oclif.config.js +2 -4
- package/oclif.manifest.json +213 -1
- package/package.json +12 -10
- package/templates/shopify/components/inputs/ShopifyMetafield.tsx +20 -0
- package/templates/shopify/docs/features.md +24 -0
- package/templates/shopify/schemaTypes/index.ts +2 -0
- package/templates/shopify/schemaTypes/objects/shopify/shopifyCollectionType.ts +18 -1
- package/templates/shopify/schemaTypes/objects/shopify/shopifyMetafieldType.ts +47 -0
- package/templates/shopify/schemaTypes/objects/shopify/shopifyProductType.ts +17 -0
- package/templates/shopify/utils/formatMetafieldValue.ts +30 -0
- package/templates/shopify-online-storefront/components/inputs/ShopifyMetafield.tsx +22 -0
- package/templates/shopify-online-storefront/schemaTypes/index.ts +4 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shop.ts +16 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shopifyCollection.ts +19 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shopifyMetafield.ts +53 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shopifyProduct.ts +19 -0
- 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
|
|
3611
|
-
--
|
|
3612
|
-
--
|
|
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
|
package/dist/SanityHelp.js
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { Help } from '@oclif/core';
|
|
2
2
|
import { getBinCommand, getRunningPackageManager } from '@sanity/cli-core/package-manager';
|
|
3
|
-
import {
|
|
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
|
package/dist/SanityHelp.js.map
CHANGED
|
@@ -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 {
|
|
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 @@
|
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
228
|
-
applicationVersion: version,
|
|
229
|
-
...config ? {
|
|
230
|
-
config
|
|
231
|
-
} : {},
|
|
244
|
+
application: null,
|
|
232
245
|
installationId,
|
|
233
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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?.();
|