@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.
- 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/init/sdkAppDependencies.js +1 -1
- package/dist/actions/init/sdkAppDependencies.js.map +1 -1
- package/dist/actions/init/studioDependencies.js +1 -1
- package/dist/actions/init/studioDependencies.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 +160 -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 +688 -476
- 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
|
@@ -4,7 +4,7 @@ import { createGzip } from 'node:zlib';
|
|
|
4
4
|
import { formatSchemaValidation, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
|
|
5
5
|
import { exitCodes } from '@sanity/cli-core';
|
|
6
6
|
import { spinner } from '@sanity/cli-core/ux';
|
|
7
|
-
import { createStudio, deployWorkbenchApp, getApplicationUrl, getWorkbench } from '@sanity/workbench-cli/deploy';
|
|
7
|
+
import { createStudio, deployWorkbenchApp, getApplicationUrl, getWorkbench, toWorkbenchPayload } from '@sanity/workbench-cli/deploy';
|
|
8
8
|
import { pack } from 'tar-fs';
|
|
9
9
|
import { createDeployment } from '../../services/userApplications.js';
|
|
10
10
|
import { getAppId } from '../../util/appId.js';
|
|
@@ -14,7 +14,7 @@ import { readIconFromPath } from '../manifest/extractCoreAppManifest.js';
|
|
|
14
14
|
import { createStudioUserApplication } from './createUserApplication.js';
|
|
15
15
|
import { checkAutoUpdates, checkBuild, checkPackageVersion, checkStudioTarget, verifyOutputDir } from './deployChecks.js';
|
|
16
16
|
import { deployDebug } from './deployDebug.js';
|
|
17
|
-
import { listDeploymentFiles,
|
|
17
|
+
import { listDeploymentFiles, reportInterfaces } from './deploymentPlan.js';
|
|
18
18
|
import { runDeploy } from './deployRunner.js';
|
|
19
19
|
import { deployStudioSchemasAndManifests } from './deployStudioSchemasAndManifests.js';
|
|
20
20
|
import { findUserApplicationForStudio } from './findUserApplication.js';
|
|
@@ -67,6 +67,7 @@ export function deployStudio(options) {
|
|
|
67
67
|
// resolve/create on user-applications, unchanged.
|
|
68
68
|
let application = null;
|
|
69
69
|
let studioCreated = false;
|
|
70
|
+
let workbenchApp;
|
|
70
71
|
if (workbench && !isExternal) {
|
|
71
72
|
reporter.report(organizationId ? {
|
|
72
73
|
message: `Organization: ${organizationId}`,
|
|
@@ -76,13 +77,13 @@ export function deployStudio(options) {
|
|
|
76
77
|
solution: 'Add `app.organizationId` to sanity.cli.ts',
|
|
77
78
|
status: 'fail'
|
|
78
79
|
});
|
|
79
|
-
await checkStudioTarget(reporter, {
|
|
80
|
+
workbenchApp = (await checkStudioTarget(reporter, {
|
|
80
81
|
appId,
|
|
81
82
|
isWorkbenchApp: true,
|
|
82
83
|
organizationId,
|
|
83
84
|
slug: workbench.slug,
|
|
84
85
|
title: appTitle
|
|
85
|
-
});
|
|
86
|
+
}))?.application;
|
|
86
87
|
} else {
|
|
87
88
|
;
|
|
88
89
|
({ application, created: studioCreated } = await resolveStudioApplication(options, {
|
|
@@ -108,14 +109,16 @@ export function deployStudio(options) {
|
|
|
108
109
|
let applicationCreated = false;
|
|
109
110
|
let rollbackApp;
|
|
110
111
|
if (!dryRun && workbench && !isExternal && organizationId && !applicationId) {
|
|
111
|
-
|
|
112
|
-
({ applicationId, rollback: rollbackApp } = await createStudio({
|
|
112
|
+
const created = await createStudio({
|
|
113
113
|
organizationId,
|
|
114
114
|
projectId,
|
|
115
115
|
slug: workbench.slug,
|
|
116
116
|
title: appTitle,
|
|
117
117
|
visibility: workbench.visibility
|
|
118
|
-
})
|
|
118
|
+
});
|
|
119
|
+
workbenchApp = created.application;
|
|
120
|
+
applicationId = created.application.id;
|
|
121
|
+
rollbackApp = created.rollback;
|
|
119
122
|
applicationCreated = true;
|
|
120
123
|
}
|
|
121
124
|
// A record created above is stranded at its slug (and blocks retries) if any
|
|
@@ -145,11 +148,29 @@ export function deployStudio(options) {
|
|
|
145
148
|
sourceDir
|
|
146
149
|
});
|
|
147
150
|
}
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
+
// An external studio hosts its own bundle, so nothing registers.
|
|
152
|
+
const interfaces = workbench && !isExternal ? reportInterfaces(reporter, workbench) : null;
|
|
153
|
+
const payload = {
|
|
154
|
+
appId: appId ?? null,
|
|
155
|
+
isAutoUpdating,
|
|
156
|
+
...organizationId ? {
|
|
157
|
+
organizationId
|
|
158
|
+
} : {},
|
|
159
|
+
...projectId ? {
|
|
160
|
+
projectId
|
|
161
|
+
} : {},
|
|
162
|
+
type: 'studio',
|
|
163
|
+
version,
|
|
164
|
+
...toWorkbenchPayload(workbench, {
|
|
165
|
+
interfaces,
|
|
166
|
+
title: appTitle
|
|
167
|
+
})
|
|
168
|
+
};
|
|
151
169
|
// Dry run stops here — everything below mutates.
|
|
152
|
-
if (dryRun) return
|
|
170
|
+
if (dryRun) return {
|
|
171
|
+
application: null,
|
|
172
|
+
payload
|
|
173
|
+
};
|
|
153
174
|
// A real deploy has already exited if anything failed; landing here without a
|
|
154
175
|
// resolved version means the deploy target was never resolved.
|
|
155
176
|
if (!version) return;
|
|
@@ -160,6 +181,7 @@ export function deployStudio(options) {
|
|
|
160
181
|
// build, so this only ships the deployment; plain studios use user-applications.
|
|
161
182
|
if (workbench && !isExternal && organizationId && applicationId) {
|
|
162
183
|
await deployWorkbenchApp({
|
|
184
|
+
access: toAccess(studioManifest),
|
|
163
185
|
app: cliConfig.app,
|
|
164
186
|
applicationId,
|
|
165
187
|
icon: appIcon,
|
|
@@ -189,20 +211,10 @@ export function deployStudio(options) {
|
|
|
189
211
|
url
|
|
190
212
|
});
|
|
191
213
|
return {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
} : {},
|
|
197
|
-
target: {
|
|
198
|
-
action: applicationCreated ? 'create' : 'update',
|
|
199
|
-
applicationId,
|
|
200
|
-
...applicationCreated ? {
|
|
201
|
-
slug: workbench.slug
|
|
202
|
-
} : {},
|
|
203
|
-
title: appTitle,
|
|
204
|
-
url
|
|
205
|
-
}
|
|
214
|
+
action: applicationCreated ? 'create' : 'update',
|
|
215
|
+
application: workbenchApp ?? null,
|
|
216
|
+
payload,
|
|
217
|
+
url
|
|
206
218
|
};
|
|
207
219
|
}
|
|
208
220
|
if (!application) return;
|
|
@@ -215,14 +227,10 @@ export function deployStudio(options) {
|
|
|
215
227
|
version
|
|
216
228
|
});
|
|
217
229
|
return {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
applicationId: application.id,
|
|
223
|
-
title: application.title ?? null,
|
|
224
|
-
url: location
|
|
225
|
-
}
|
|
230
|
+
action: studioCreated ? 'create' : 'update',
|
|
231
|
+
application,
|
|
232
|
+
payload,
|
|
233
|
+
url: location
|
|
226
234
|
};
|
|
227
235
|
} catch (err) {
|
|
228
236
|
await rollbackApp?.();
|
|
@@ -305,8 +313,8 @@ export function deployStudio(options) {
|
|
|
305
313
|
}, output);
|
|
306
314
|
} catch (error) {
|
|
307
315
|
deployDebug('Error deploying studio schemas and manifests', error);
|
|
308
|
-
if (error instanceof SchemaExtractionError) {
|
|
309
|
-
output.error(formatSchemaValidation(error.validation
|
|
316
|
+
if (error instanceof SchemaExtractionError && error.validation?.length) {
|
|
317
|
+
output.error(formatSchemaValidation(error.validation), {
|
|
310
318
|
exit: 1
|
|
311
319
|
});
|
|
312
320
|
}
|
|
@@ -366,6 +374,18 @@ export default defineCliConfig({
|
|
|
366
374
|
output.log(`\n${example}`);
|
|
367
375
|
return location;
|
|
368
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* One `datasets` access entry per unique workspace dataset, with a `resourceId`
|
|
379
|
+
* of `"<projectId>.<dataset>"`. Deduped, since workspaces can share a dataset.
|
|
380
|
+
*/ function toAccess(manifest) {
|
|
381
|
+
const resourceIds = new Set((manifest?.workspaces ?? []).map((w)=>`${w.projectId}.${w.dataset}`));
|
|
382
|
+
return [
|
|
383
|
+
...resourceIds
|
|
384
|
+
].map((resourceId)=>({
|
|
385
|
+
resourceId,
|
|
386
|
+
resourceType: 'dataset'
|
|
387
|
+
}));
|
|
388
|
+
}
|
|
369
389
|
function toWorkspaces(manifest) {
|
|
370
390
|
return (manifest?.workspaces ?? []).map((workspace)=>({
|
|
371
391
|
basePath: workspace.basePath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/deployStudio.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip, type Gzip} from 'node:zlib'\n\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {\n type BrettWorkspace,\n createStudio,\n deployWorkbenchApp,\n getApplicationUrl,\n getWorkbench,\n} from '@sanity/workbench-cli/deploy'\nimport {type StudioManifest} from 'sanity'\nimport {pack} from 'tar-fs'\n\nimport {createDeployment, type UserApplication} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_ORGANIZATION_ID, NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {buildStudio} from '../build/buildStudio.js'\nimport {readIconFromPath} from '../manifest/extractCoreAppManifest.js'\nimport {createStudioUserApplication} from './createUserApplication.js'\nimport {\n checkAutoUpdates,\n checkBuild,\n checkPackageVersion,\n checkStudioTarget,\n type DeployCheckReporter,\n verifyOutputDir,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {listDeploymentFiles, reportExposes} from './deploymentPlan.js'\nimport {type DeployResult, runDeploy} from './deployRunner.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\n\nconst STUDIO_PACKAGE = 'sanity'\n\nexport function deployStudio(options: DeployAppOptions): Promise<void> {\n return runDeploy(options, {\n listFiles: ({flags, projectRoot, sourceDir}) =>\n flags.external ? Promise.resolve([]) : listDeploymentFiles(sourceDir, projectRoot.directory),\n run: runStudioDeployment,\n type: 'studio',\n })\n}\n\n/** Validates the deploy, extracts and uploads the schema, and ships the build. */\nasync function runStudioDeployment(\n options: DeployAppOptions,\n reporter: DeployCheckReporter,\n): Promise<DeployResult | void> {\n const {cliConfig, flags, output, sourceDir} = options\n const workDir = options.projectRoot.directory\n const isExternal = !!flags.external\n const workbench = getWorkbench(cliConfig)\n const isWorkbenchApp = workbench !== null\n const projectId = cliConfig.api?.projectId\n const organizationId = cliConfig.app?.organizationId\n const appId = getAppId(cliConfig)\n const dryRun = !!flags['dry-run']\n\n // A federated app deploys through Sanity's build/hosting pipeline, which\n // --external skips — fail before doing any other work.\n if (isExternal && isWorkbenchApp) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'Deploying a federated application to an external host is not yet supported',\n solution: 'Remove the --external flag to deploy to Sanity hosting',\n status: 'fail',\n })\n }\n\n const appTitle = workbench\n ? flags.title?.trim() || cliConfig.app?.title?.trim() || workbench.slug\n : ''\n\n const isAutoUpdating = checkAutoUpdates(reporter, {cliConfig, flags})\n\n const version = await checkPackageVersion(reporter, {\n moduleName: STUDIO_PACKAGE,\n workDir,\n })\n\n reporter.report(\n projectId\n ? {message: `Project: ${projectId}`, status: 'pass'}\n : {\n message: NO_PROJECT_ID,\n solution: 'Add `api.projectId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n\n // Workbench studios deploy to Brett (which needs the org); plain studios\n // resolve/create on user-applications, unchanged.\n let application: UserApplication | null = null\n let studioCreated = false\n if (workbench && !isExternal) {\n reporter.report(\n organizationId\n ? {message: `Organization: ${organizationId}`, status: 'pass'}\n : {\n message: NO_ORGANIZATION_ID,\n solution: 'Add `app.organizationId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n await checkStudioTarget(reporter, {\n appId,\n isWorkbenchApp: true,\n organizationId,\n slug: workbench.slug,\n title: appTitle,\n })\n } else {\n ;({application, created: studioCreated} = await resolveStudioApplication(options, {\n dryRun,\n reporter,\n }))\n }\n\n // A first deploy mints the app id and the build inlines it; --no-build would\n // ship an existing bundle carrying a different id, so it can't be a first deploy.\n if (workbench && !isExternal && !appId && !flags.build) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'A first deploy cannot skip the build (--no-build)',\n solution: 'Drop --no-build so the new application id is inlined into the build',\n status: 'fail',\n })\n }\n\n // Read up front so a bad icon path fails before we create or build.\n const appIcon =\n !dryRun && !isExternal && workbench?.icon\n ? await readIconFromPath(workDir, workbench.icon)\n : undefined\n\n // Create the studio before the build so the bundle carries its real id. A\n // redeploy already has it from `deployment.appId`; a dry run skips creation.\n let applicationId = appId\n let applicationCreated = false\n let rollbackApp: (() => Promise<void>) | undefined\n if (!dryRun && workbench && !isExternal && organizationId && !applicationId) {\n ;({applicationId, rollback: rollbackApp} = await createStudio({\n organizationId,\n projectId,\n slug: workbench.slug,\n title: appTitle,\n visibility: workbench.visibility,\n }))\n applicationCreated = true\n }\n\n // A record created above is stranded at its slug (and blocks retries) if any\n // step before it fully deploys fails, so undo the creation on failure.\n try {\n await checkBuild(reporter, {\n build: () =>\n buildStudio({\n applicationId: workbench ? applicationId : undefined,\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n }),\n skipReason: studioBuildSkipReason({build: flags.build, isExternal}),\n successMessage: 'Studio built',\n })\n\n if (!isExternal) {\n await verifyOutputDir({isWorkbenchApp, reporter, sourceDir})\n }\n\n // Report the exposes deploying with the studio, both modes. External studios\n // host their own bundle, so nothing registers.\n const exposes = workbench && !isExternal ? reportExposes(reporter, workbench) : []\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return\n\n // A real deploy has already exited if anything failed; landing here without a\n // resolved version means the deploy target was never resolved.\n if (!version) return\n\n const studioManifest = await uploadStudioSchema(options, {isExternal})\n // The studio was created (or resolved from `deployment.appId`) before the\n // build, so this only ships the deployment; plain studios use user-applications.\n if (workbench && !isExternal && organizationId && applicationId) {\n await deployWorkbenchApp({\n app: cliConfig.app,\n applicationId,\n icon: appIcon,\n isApp: false,\n isAutoUpdating,\n label: 'Deploying studio',\n // Once the deployment is live, a metadata-sync failure must not delete\n // the studio.\n onDeployed: () => {\n rollbackApp = undefined\n },\n sourceDir,\n title: appTitle,\n version,\n visibility: workbench.visibility,\n workspaces: toWorkspaces(studioManifest),\n })\n const url = getApplicationUrl({id: applicationId, organizationId, type: 'studio'})\n logWorkbenchStudioDeployed({applicationId, cliConfig, output, url})\n return {\n applicationType: 'studio',\n applicationVersion: version,\n ...(exposes.length > 0 ? {exposes} : {}),\n target: {\n action: applicationCreated ? 'create' : 'update',\n applicationId,\n ...(applicationCreated ? {slug: workbench.slug} : {}),\n title: appTitle,\n url,\n },\n }\n }\n\n if (!application) return\n const location = await shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n })\n\n return {\n applicationType: 'studio',\n applicationVersion: version,\n target: {\n action: studioCreated ? 'create' : 'update',\n applicationId: application.id,\n title: application.title ?? null,\n url: location,\n },\n }\n } catch (err) {\n await rollbackApp?.()\n throw err\n }\n}\n\n/**\n * Finds the application a real deploy targets, registering a studio host when\n * none is configured. A dry run resolves and reports the target read-only instead.\n */\nasync function resolveStudioApplication(\n options: DeployAppOptions,\n {dryRun, reporter}: {dryRun: boolean; reporter: DeployCheckReporter},\n): Promise<{application: UserApplication | null; created: boolean}> {\n const {cliConfig, flags, output} = options\n const isExternal = !!flags.external\n const appId = getAppId(cliConfig)\n // Sets the title on a newly registered studio; blank falls back to undefined\n const title = flags.title?.trim() || undefined\n\n if (dryRun) {\n await checkStudioTarget(reporter, {\n appId,\n isExternal,\n projectId: cliConfig.api?.projectId,\n studioHost: cliConfig.studioHost,\n title,\n urlFlag: flags.url,\n })\n return {application: null, created: false}\n }\n\n const projectId = cliConfig.api?.projectId ?? ''\n // `created` is true when a configured-but-unregistered host was just registered.\n const {application, created} = await findUserApplicationForStudio({\n appId,\n isExternal,\n output,\n projectId,\n studioHost: cliConfig.studioHost,\n title,\n unattended: !!flags.yes,\n urlFlag: flags.url,\n })\n\n if (!application) {\n if (isExternal) {\n output.log('Your project has not been registered with an external studio URL.')\n output.log('Please enter the full URL where your studio is hosted.')\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log('To deploy your Sanity Studio to our hosted sanity.studio service,')\n output.log('you will need one. Please enter the subdomain you want to use.')\n }\n\n const registered = await createStudioUserApplication({\n projectId,\n title,\n urlType: isExternal ? 'external' : 'internal',\n })\n deployDebug('Created user application', registered)\n return {application: registered, created: true}\n }\n\n deployDebug('Found user application', application)\n return {application, created}\n}\n\n/** Extracts the studio schema and manifest and uploads them to the schema store. */\nasync function uploadStudioSchema(\n options: DeployAppOptions,\n {isExternal}: {isExternal: boolean},\n): Promise<StudioManifest | null> {\n const {cliConfig, flags, output, projectRoot, sourceDir} = options\n\n let studioManifest: StudioManifest | null = null\n try {\n studioManifest = await deployStudioSchemasAndManifests(\n {\n configPath: projectRoot.path,\n isExternal,\n outPath: `${sourceDir}/static`,\n projectId: cliConfig.api?.projectId ?? '',\n schemaRequired: flags['schema-required'],\n verbose: flags.verbose,\n workDir: projectRoot.directory,\n },\n output,\n )\n } catch (error) {\n deployDebug('Error deploying studio schemas and manifests', error)\n if (error instanceof SchemaExtractionError) {\n output.error(formatSchemaValidation(error.validation || []), {exit: 1})\n }\n output.error(`Error deploying studio schemas and manifests: ${error}`, {exit: 1})\n }\n\n if (!studioManifest) {\n output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {\n exit: 1,\n })\n }\n\n return studioManifest\n}\n\nasync function shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n}: {\n application: UserApplication\n isAutoUpdating: boolean\n isExternal: boolean\n options: DeployAppOptions\n studioManifest: StudioManifest | null\n version: string\n}): Promise<string> {\n const {cliConfig, output, sourceDir} = options\n\n let tarball: Gzip | undefined\n if (!isExternal) {\n tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n }\n\n const spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start()\n let location: string\n try {\n ;({location} = await createDeployment({\n applicationId: application.id,\n isApp: false,\n isAutoUpdating,\n manifest: studioManifest,\n projectId: cliConfig.api?.projectId,\n tarball,\n version,\n }))\n } catch (error) {\n spin.fail()\n throw error\n }\n spin.succeed()\n\n const named = application.title ? ` — \"${application.title}\"` : ''\n output.log(\n isExternal\n ? `\\nSuccess! Studio registered${named}`\n : `\\nSuccess! Studio deployed to ${styleText('cyan', location)}${named}`,\n )\n\n if (getAppId(cliConfig)) return location\n\n const example = `Example:\nexport default defineCliConfig({\n //…\n deployment: {\n ${styleText('cyan', `appId: '${application.id}'`)},\n },\n //…\n})`\n output.log(`\\nAdd ${styleText('cyan', `appId: '${application.id}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n output.log(`\\n${example}`)\n\n return location\n}\n\nfunction toWorkspaces(manifest: StudioManifest | null): BrettWorkspace[] {\n return (manifest?.workspaces ?? []).map((workspace) => ({\n basePath: workspace.basePath,\n dataset: workspace.dataset,\n icon: workspace.icon,\n name: workspace.name,\n projectId: workspace.projectId,\n schemaDescriptorId: workspace.schemaDescriptorId,\n subtitle: workspace.subtitle,\n title: workspace.title,\n }))\n}\n\n/** Renders the workbench studio's deploy result; the appId hint shows only when none is configured. */\nfunction logWorkbenchStudioDeployed({\n applicationId,\n cliConfig,\n output,\n url,\n}: {\n applicationId: string\n cliConfig: DeployAppOptions['cliConfig']\n output: DeployAppOptions['output']\n url: string\n}): void {\n output.log(`\\nSuccess! Studio deployed to ${styleText('cyan', url)}`)\n if (getAppId(cliConfig)) return\n\n output.log(`\\nAdd ${styleText('cyan', `appId: '${applicationId}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n}\n\nfunction studioBuildSkipReason({build, isExternal}: {build: boolean; isExternal: boolean}) {\n if (isExternal) return 'Build skipped for externally hosted studios'\n if (!build) return 'Build skipped (--no-build) — validating existing output directory'\n return\n}\n"],"names":["basename","dirname","styleText","createGzip","formatSchemaValidation","SchemaExtractionError","exitCodes","spinner","createStudio","deployWorkbenchApp","getApplicationUrl","getWorkbench","pack","createDeployment","getAppId","NO_ORGANIZATION_ID","NO_PROJECT_ID","buildStudio","readIconFromPath","createStudioUserApplication","checkAutoUpdates","checkBuild","checkPackageVersion","checkStudioTarget","verifyOutputDir","deployDebug","listDeploymentFiles","reportExposes","runDeploy","deployStudioSchemasAndManifests","findUserApplicationForStudio","STUDIO_PACKAGE","deployStudio","options","listFiles","flags","projectRoot","sourceDir","external","Promise","resolve","directory","run","runStudioDeployment","type","reporter","cliConfig","output","workDir","isExternal","workbench","isWorkbenchApp","projectId","api","organizationId","app","appId","dryRun","report","exitCode","USAGE_ERROR","message","solution","status","appTitle","title","trim","slug","isAutoUpdating","version","moduleName","application","studioCreated","created","resolveStudioApplication","build","appIcon","icon","undefined","applicationId","applicationCreated","rollbackApp","rollback","visibility","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","studioBuildSkipReason","successMessage","exposes","studioManifest","uploadStudioSchema","isApp","label","onDeployed","workspaces","toWorkspaces","url","id","logWorkbenchStudioDeployed","applicationType","applicationVersion","length","target","action","location","shipStudioDeployment","err","studioHost","urlFlag","unattended","yes","log","registered","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","exit","tarball","entries","pipe","spin","start","manifest","fail","succeed","named","example","map","workspace","basePath","dataset","name","schemaDescriptorId","subtitle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/deployStudio.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip, type Gzip} from 'node:zlib'\n\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {\n type BrettAccess,\n type BrettWorkspace,\n createStudio,\n deployWorkbenchApp,\n getApplicationUrl,\n getWorkbench,\n toWorkbenchPayload,\n} from '@sanity/workbench-cli/deploy'\nimport {type StudioManifest} from 'sanity'\nimport {pack} from 'tar-fs'\n\nimport {createDeployment, type UserApplication} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_ORGANIZATION_ID, NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {buildStudio} from '../build/buildStudio.js'\nimport {readIconFromPath} from '../manifest/extractCoreAppManifest.js'\nimport {createStudioUserApplication} from './createUserApplication.js'\nimport {\n checkAutoUpdates,\n checkBuild,\n checkPackageVersion,\n checkStudioTarget,\n type DeployCheckReporter,\n verifyOutputDir,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {listDeploymentFiles, reportInterfaces} from './deploymentPlan.js'\nimport {type DeployPayload, type DeployResult, runDeploy} from './deployRunner.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\n\nconst STUDIO_PACKAGE = 'sanity'\n\nexport function deployStudio(options: DeployAppOptions): Promise<void> {\n return runDeploy(options, {\n listFiles: ({flags, projectRoot, sourceDir}) =>\n flags.external ? Promise.resolve([]) : listDeploymentFiles(sourceDir, projectRoot.directory),\n run: runStudioDeployment,\n type: 'studio',\n })\n}\n\n/** Validates the deploy, extracts and uploads the schema, and ships the build. */\nasync function runStudioDeployment(\n options: DeployAppOptions,\n reporter: DeployCheckReporter,\n): Promise<DeployResult | void> {\n const {cliConfig, flags, output, sourceDir} = options\n const workDir = options.projectRoot.directory\n const isExternal = !!flags.external\n const workbench = getWorkbench(cliConfig)\n const isWorkbenchApp = workbench !== null\n const projectId = cliConfig.api?.projectId\n const organizationId = cliConfig.app?.organizationId\n const appId = getAppId(cliConfig)\n const dryRun = !!flags['dry-run']\n\n // A federated app deploys through Sanity's build/hosting pipeline, which\n // --external skips — fail before doing any other work.\n if (isExternal && isWorkbenchApp) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'Deploying a federated application to an external host is not yet supported',\n solution: 'Remove the --external flag to deploy to Sanity hosting',\n status: 'fail',\n })\n }\n\n const appTitle = workbench\n ? flags.title?.trim() || cliConfig.app?.title?.trim() || workbench.slug\n : ''\n\n const isAutoUpdating = checkAutoUpdates(reporter, {cliConfig, flags})\n\n const version = await checkPackageVersion(reporter, {\n moduleName: STUDIO_PACKAGE,\n workDir,\n })\n\n reporter.report(\n projectId\n ? {message: `Project: ${projectId}`, status: 'pass'}\n : {\n message: NO_PROJECT_ID,\n solution: 'Add `api.projectId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n\n // Workbench studios deploy to Brett (which needs the org); plain studios\n // resolve/create on user-applications, unchanged.\n let application: UserApplication | null = null\n let studioCreated = false\n let workbenchApp: object | undefined\n if (workbench && !isExternal) {\n reporter.report(\n organizationId\n ? {message: `Organization: ${organizationId}`, status: 'pass'}\n : {\n message: NO_ORGANIZATION_ID,\n solution: 'Add `app.organizationId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n workbenchApp = (\n await checkStudioTarget(reporter, {\n appId,\n isWorkbenchApp: true,\n organizationId,\n slug: workbench.slug,\n title: appTitle,\n })\n )?.application\n } else {\n ;({application, created: studioCreated} = await resolveStudioApplication(options, {\n dryRun,\n reporter,\n }))\n }\n\n // A first deploy mints the app id and the build inlines it; --no-build would\n // ship an existing bundle carrying a different id, so it can't be a first deploy.\n if (workbench && !isExternal && !appId && !flags.build) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'A first deploy cannot skip the build (--no-build)',\n solution: 'Drop --no-build so the new application id is inlined into the build',\n status: 'fail',\n })\n }\n\n // Read up front so a bad icon path fails before we create or build.\n const appIcon =\n !dryRun && !isExternal && workbench?.icon\n ? await readIconFromPath(workDir, workbench.icon)\n : undefined\n\n // Create the studio before the build so the bundle carries its real id. A\n // redeploy already has it from `deployment.appId`; a dry run skips creation.\n let applicationId = appId\n let applicationCreated = false\n let rollbackApp: (() => Promise<void>) | undefined\n if (!dryRun && workbench && !isExternal && organizationId && !applicationId) {\n const created = await createStudio({\n organizationId,\n projectId,\n slug: workbench.slug,\n title: appTitle,\n visibility: workbench.visibility,\n })\n workbenchApp = created.application\n applicationId = created.application.id\n rollbackApp = created.rollback\n applicationCreated = true\n }\n\n // A record created above is stranded at its slug (and blocks retries) if any\n // step before it fully deploys fails, so undo the creation on failure.\n try {\n await checkBuild(reporter, {\n build: () =>\n buildStudio({\n applicationId: workbench ? applicationId : undefined,\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n }),\n skipReason: studioBuildSkipReason({build: flags.build, isExternal}),\n successMessage: 'Studio built',\n })\n\n if (!isExternal) {\n await verifyOutputDir({isWorkbenchApp, reporter, sourceDir})\n }\n\n // An external studio hosts its own bundle, so nothing registers.\n const interfaces = workbench && !isExternal ? reportInterfaces(reporter, workbench) : null\n\n const payload: DeployPayload = {\n appId: appId ?? null,\n isAutoUpdating,\n ...(organizationId ? {organizationId} : {}),\n ...(projectId ? {projectId} : {}),\n type: 'studio',\n version,\n ...toWorkbenchPayload(workbench, {interfaces, title: appTitle}),\n }\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return {application: null, payload}\n\n // A real deploy has already exited if anything failed; landing here without a\n // resolved version means the deploy target was never resolved.\n if (!version) return\n\n const studioManifest = await uploadStudioSchema(options, {isExternal})\n // The studio was created (or resolved from `deployment.appId`) before the\n // build, so this only ships the deployment; plain studios use user-applications.\n if (workbench && !isExternal && organizationId && applicationId) {\n await deployWorkbenchApp({\n access: toAccess(studioManifest),\n app: cliConfig.app,\n applicationId,\n icon: appIcon,\n isApp: false,\n isAutoUpdating,\n label: 'Deploying studio',\n // Once the deployment is live, a metadata-sync failure must not delete\n // the studio.\n onDeployed: () => {\n rollbackApp = undefined\n },\n sourceDir,\n title: appTitle,\n version,\n visibility: workbench.visibility,\n workspaces: toWorkspaces(studioManifest),\n })\n const url = getApplicationUrl({id: applicationId, organizationId, type: 'studio'})\n logWorkbenchStudioDeployed({applicationId, cliConfig, output, url})\n return {\n action: applicationCreated ? 'create' : 'update',\n application: workbenchApp ?? null,\n payload,\n url,\n }\n }\n\n if (!application) return\n const location = await shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n })\n\n return {\n action: studioCreated ? 'create' : 'update',\n application,\n payload,\n url: location,\n }\n } catch (err) {\n await rollbackApp?.()\n throw err\n }\n}\n\n/**\n * Finds the application a real deploy targets, registering a studio host when\n * none is configured. A dry run resolves and reports the target read-only instead.\n */\nasync function resolveStudioApplication(\n options: DeployAppOptions,\n {dryRun, reporter}: {dryRun: boolean; reporter: DeployCheckReporter},\n): Promise<{application: UserApplication | null; created: boolean}> {\n const {cliConfig, flags, output} = options\n const isExternal = !!flags.external\n const appId = getAppId(cliConfig)\n // Sets the title on a newly registered studio; blank falls back to undefined\n const title = flags.title?.trim() || undefined\n\n if (dryRun) {\n await checkStudioTarget(reporter, {\n appId,\n isExternal,\n projectId: cliConfig.api?.projectId,\n studioHost: cliConfig.studioHost,\n title,\n urlFlag: flags.url,\n })\n return {application: null, created: false}\n }\n\n const projectId = cliConfig.api?.projectId ?? ''\n // `created` is true when a configured-but-unregistered host was just registered.\n const {application, created} = await findUserApplicationForStudio({\n appId,\n isExternal,\n output,\n projectId,\n studioHost: cliConfig.studioHost,\n title,\n unattended: !!flags.yes,\n urlFlag: flags.url,\n })\n\n if (!application) {\n if (isExternal) {\n output.log('Your project has not been registered with an external studio URL.')\n output.log('Please enter the full URL where your studio is hosted.')\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log('To deploy your Sanity Studio to our hosted sanity.studio service,')\n output.log('you will need one. Please enter the subdomain you want to use.')\n }\n\n const registered = await createStudioUserApplication({\n projectId,\n title,\n urlType: isExternal ? 'external' : 'internal',\n })\n deployDebug('Created user application', registered)\n return {application: registered, created: true}\n }\n\n deployDebug('Found user application', application)\n return {application, created}\n}\n\n/** Extracts the studio schema and manifest and uploads them to the schema store. */\nasync function uploadStudioSchema(\n options: DeployAppOptions,\n {isExternal}: {isExternal: boolean},\n): Promise<StudioManifest | null> {\n const {cliConfig, flags, output, projectRoot, sourceDir} = options\n\n let studioManifest: StudioManifest | null = null\n try {\n studioManifest = await deployStudioSchemasAndManifests(\n {\n configPath: projectRoot.path,\n isExternal,\n outPath: `${sourceDir}/static`,\n projectId: cliConfig.api?.projectId ?? '',\n schemaRequired: flags['schema-required'],\n verbose: flags.verbose,\n workDir: projectRoot.directory,\n },\n output,\n )\n } catch (error) {\n deployDebug('Error deploying studio schemas and manifests', error)\n if (error instanceof SchemaExtractionError && error.validation?.length) {\n output.error(formatSchemaValidation(error.validation), {exit: 1})\n }\n output.error(`Error deploying studio schemas and manifests: ${error}`, {exit: 1})\n }\n\n if (!studioManifest) {\n output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {\n exit: 1,\n })\n }\n\n return studioManifest\n}\n\nasync function shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n}: {\n application: UserApplication\n isAutoUpdating: boolean\n isExternal: boolean\n options: DeployAppOptions\n studioManifest: StudioManifest | null\n version: string\n}): Promise<string> {\n const {cliConfig, output, sourceDir} = options\n\n let tarball: Gzip | undefined\n if (!isExternal) {\n tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n }\n\n const spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start()\n let location: string\n try {\n ;({location} = await createDeployment({\n applicationId: application.id,\n isApp: false,\n isAutoUpdating,\n manifest: studioManifest,\n projectId: cliConfig.api?.projectId,\n tarball,\n version,\n }))\n } catch (error) {\n spin.fail()\n throw error\n }\n spin.succeed()\n\n const named = application.title ? ` — \"${application.title}\"` : ''\n output.log(\n isExternal\n ? `\\nSuccess! Studio registered${named}`\n : `\\nSuccess! Studio deployed to ${styleText('cyan', location)}${named}`,\n )\n\n if (getAppId(cliConfig)) return location\n\n const example = `Example:\nexport default defineCliConfig({\n //…\n deployment: {\n ${styleText('cyan', `appId: '${application.id}'`)},\n },\n //…\n})`\n output.log(`\\nAdd ${styleText('cyan', `appId: '${application.id}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n output.log(`\\n${example}`)\n\n return location\n}\n\n/**\n * One `datasets` access entry per unique workspace dataset, with a `resourceId`\n * of `\"<projectId>.<dataset>\"`. Deduped, since workspaces can share a dataset.\n */\nfunction toAccess(manifest: StudioManifest | null): BrettAccess[] {\n const resourceIds = new Set(\n (manifest?.workspaces ?? []).map((w) => `${w.projectId}.${w.dataset}`),\n )\n return [...resourceIds].map(\n (resourceId) => ({resourceId, resourceType: 'dataset'}) satisfies BrettAccess,\n )\n}\n\nfunction toWorkspaces(manifest: StudioManifest | null): BrettWorkspace[] {\n return (manifest?.workspaces ?? []).map((workspace) => ({\n basePath: workspace.basePath,\n dataset: workspace.dataset,\n icon: workspace.icon,\n name: workspace.name,\n projectId: workspace.projectId,\n schemaDescriptorId: workspace.schemaDescriptorId,\n subtitle: workspace.subtitle,\n title: workspace.title,\n }))\n}\n\n/** Renders the workbench studio's deploy result; the appId hint shows only when none is configured. */\nfunction logWorkbenchStudioDeployed({\n applicationId,\n cliConfig,\n output,\n url,\n}: {\n applicationId: string\n cliConfig: DeployAppOptions['cliConfig']\n output: DeployAppOptions['output']\n url: string\n}): void {\n output.log(`\\nSuccess! Studio deployed to ${styleText('cyan', url)}`)\n if (getAppId(cliConfig)) return\n\n output.log(`\\nAdd ${styleText('cyan', `appId: '${applicationId}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n}\n\nfunction studioBuildSkipReason({build, isExternal}: {build: boolean; isExternal: boolean}) {\n if (isExternal) return 'Build skipped for externally hosted studios'\n if (!build) return 'Build skipped (--no-build) — validating existing output directory'\n return\n}\n"],"names":["basename","dirname","styleText","createGzip","formatSchemaValidation","SchemaExtractionError","exitCodes","spinner","createStudio","deployWorkbenchApp","getApplicationUrl","getWorkbench","toWorkbenchPayload","pack","createDeployment","getAppId","NO_ORGANIZATION_ID","NO_PROJECT_ID","buildStudio","readIconFromPath","createStudioUserApplication","checkAutoUpdates","checkBuild","checkPackageVersion","checkStudioTarget","verifyOutputDir","deployDebug","listDeploymentFiles","reportInterfaces","runDeploy","deployStudioSchemasAndManifests","findUserApplicationForStudio","STUDIO_PACKAGE","deployStudio","options","listFiles","flags","projectRoot","sourceDir","external","Promise","resolve","directory","run","runStudioDeployment","type","reporter","cliConfig","output","workDir","isExternal","workbench","isWorkbenchApp","projectId","api","organizationId","app","appId","dryRun","report","exitCode","USAGE_ERROR","message","solution","status","appTitle","title","trim","slug","isAutoUpdating","version","moduleName","application","studioCreated","workbenchApp","created","resolveStudioApplication","build","appIcon","icon","undefined","applicationId","applicationCreated","rollbackApp","visibility","id","rollback","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","studioBuildSkipReason","successMessage","interfaces","payload","studioManifest","uploadStudioSchema","access","toAccess","isApp","label","onDeployed","workspaces","toWorkspaces","url","logWorkbenchStudioDeployed","action","location","shipStudioDeployment","err","studioHost","urlFlag","unattended","yes","log","registered","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","length","exit","tarball","entries","pipe","spin","start","manifest","fail","succeed","named","example","resourceIds","Set","map","w","dataset","resourceId","resourceType","workspace","basePath","name","schemaDescriptorId","subtitle"],"mappings":"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"}
|
|
@@ -50,42 +50,30 @@ function totalBytes(files) {
|
|
|
50
50
|
if (check.status === 'fail') errors[check.message] = check.solution ?? null;
|
|
51
51
|
else if (check.status === 'warn') warnings.push(check.message);
|
|
52
52
|
}
|
|
53
|
-
// `exposes`, `config` and `isSingleton` are workbench-only; plain
|
|
54
|
-
// apps (and apps that don't set the flag) omit them.
|
|
55
53
|
return {
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
action: plan.target?.action ?? null,
|
|
55
|
+
// Only a successful deploy reports the backend's record.
|
|
56
|
+
application: null,
|
|
57
|
+
canDeploy: isDeployable(plan),
|
|
58
58
|
errors,
|
|
59
|
-
...plan.exposes.length > 0 ? {
|
|
60
|
-
exposes: plan.exposes
|
|
61
|
-
} : {},
|
|
62
59
|
files: plan.files,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
} : {},
|
|
66
|
-
isDeployable: isDeployable(plan),
|
|
67
|
-
...plan.isSingleton === undefined ? {} : {
|
|
68
|
-
isSingleton: plan.isSingleton
|
|
69
|
-
},
|
|
70
|
-
target: plan.target,
|
|
60
|
+
payload: plan.payload,
|
|
61
|
+
reason: plan.checks.find((check)=>check.status === 'fail')?.message ?? null,
|
|
71
62
|
totalBytes: totalBytes(plan.files),
|
|
63
|
+
url: plan.target?.url ?? null,
|
|
72
64
|
warnings
|
|
73
65
|
};
|
|
74
66
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
status: 'pass'
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
return exposes;
|
|
67
|
+
export function reportInterfaces(reporter, app) {
|
|
68
|
+
const { lines, services, views } = summarizeInterfaces(app);
|
|
69
|
+
for (const message of lines)reporter.report({
|
|
70
|
+
message,
|
|
71
|
+
status: 'pass'
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
services,
|
|
75
|
+
views
|
|
76
|
+
};
|
|
89
77
|
}
|
|
90
78
|
export function renderDeploymentPlan(plan, output) {
|
|
91
79
|
const label = plan.type === 'coreApp' ? 'application' : 'studio';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/deploymentPlan.ts"],"sourcesContent":["import {readdir, stat} from 'node:fs/promises'\nimport {join, relative, sep} from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\nimport {type DeployedExpose, summarizeInterfaces} from '@sanity/workbench-cli/deploy'\n\nimport {checkStatusIcon, nestLines, renderIssues} from '../../util/checks.js'\nimport {pluralize} from '../../util/pluralize.js'\nimport {type DeployCheck, type DeployCheckReporter, type DeployTarget} from './deployChecks.js'\n\nexport interface DeploymentFile {\n /** Path relative to the project root, POSIX-style. */\n path: string\n size: number\n}\n\n/** What a `--dry-run` deploy would do: the real deploy sequence with every mutation gated off. */\nexport interface DeploymentPlan {\n checks: DeployCheck[]\n /** Media-library config summary; `null` unless a config deploys. */\n config: string | null\n /** Workbench views and services registered with the deploy. */\n exposes: DeployedExpose[]\n files: DeploymentFile[]\n /** The resolved deploy target; `null` when the checks can't determine one. */\n target: DeployTarget | null\n type: 'coreApp' | 'studio'\n /** Installed framework version the deploy would use; `null` when not found. */\n version: string | null\n\n /** The app's explicit `isSingleton` flag; `undefined` when the app doesn't set it. */\n isSingleton?: boolean\n}\n\n/**\n * Lists the files a deploy would pack from `sourceDir`, as paths relative to\n * `fromDir`. A missing directory yields an empty list rather than throwing.\n */\nexport async function listDeploymentFiles(\n sourceDir: string,\n fromDir: string,\n): Promise<DeploymentFile[]> {\n const walk = async (dir: string): Promise<string[]> => {\n let entries\n try {\n entries = await readdir(dir, {withFileTypes: true})\n } catch {\n return []\n }\n const nested = await Promise.all(\n entries.map((entry) => {\n const full = join(dir, entry.name)\n return entry.isDirectory() ? walk(full) : Promise.resolve([full])\n }),\n )\n return nested.flat()\n }\n\n const absolute = await walk(sourceDir)\n const files = await Promise.all(\n absolute.map(async (file) => ({\n // Deploy paths are POSIX-style regardless of the host OS (Windows gives `\\`).\n path: relative(fromDir, file).split(sep).join('/'),\n size: (await stat(file)).size,\n })),\n )\n return files.toSorted((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function isDeployable(plan: DeploymentPlan): boolean {\n return plan.checks.every((check) => check.status !== 'fail')\n}\n\nfunction totalBytes(files: DeploymentFile[]): number {\n return files.reduce((sum, file) => sum + file.size, 0)\n}\n\n/**\n * A problem-focused, machine-readable projection of the plan: blocking problems\n * mapped to their fix, warnings as messages. Derived from the same checks the\n * human report renders (its pass/skip lines are informational and omitted here).\n */\nexport function deploymentPlanToJson(plan: DeploymentPlan): {\n applicationType: DeploymentPlan['type']\n applicationVersion: string | null\n config?: string\n errors: Record<string, string | null>\n exposes?: DeployedExpose[]\n files: DeploymentFile[]\n isDeployable: boolean\n isSingleton?: boolean\n target: DeployTarget | null\n totalBytes: number\n warnings: string[]\n} {\n const errors: Record<string, string | null> = {}\n const warnings: string[] = []\n for (const check of plan.checks) {\n if (check.status === 'fail') errors[check.message] = check.solution ?? null\n else if (check.status === 'warn') warnings.push(check.message)\n }\n\n // `exposes`, `config` and `isSingleton` are workbench-only; plain\n // apps (and apps that don't set the flag) omit them.\n return {\n applicationType: plan.type,\n applicationVersion: plan.version,\n errors,\n ...(plan.exposes.length > 0 ? {exposes: plan.exposes} : {}),\n files: plan.files,\n ...(plan.config ? {config: plan.config} : {}),\n isDeployable: isDeployable(plan),\n ...(plan.isSingleton === undefined ? {} : {isSingleton: plan.isSingleton}),\n target: plan.target,\n totalBytes: totalBytes(plan.files),\n warnings,\n }\n}\n\n/**\n * Reports an app's exposes as pass checks and returns them structured for the\n * `--json` payload. The structured list rides on the first check so a dry run's\n * collector can read it back.\n */\nexport function reportExposes(\n reporter: DeployCheckReporter,\n app: Parameters<typeof summarizeInterfaces>[0],\n): DeployedExpose[] {\n const {exposes, lines} = summarizeInterfaces(app)\n for (const [index, message] of lines.entries()) {\n reporter.report({exposes: index === 0 ? exposes : undefined, message, status: 'pass'})\n }\n return exposes\n}\n\nexport function renderDeploymentPlan(plan: DeploymentPlan, output: Output): void {\n const label = plan.type === 'coreApp' ? 'application' : 'studio'\n const problems = plan.checks.filter((check) => check.status === 'fail')\n const warnings = plan.checks.filter((check) => check.status === 'warn')\n\n output.log('\\nDry run — no changes made.\\n')\n\n // Only pass/skip here; problems and warnings render below with their fixes.\n for (const check of plan.checks) {\n if (check.status === 'pass' || check.status === 'skip') {\n output.log(nestLines(` ${checkStatusIcon(check.status)} ${check.message}`))\n }\n }\n\n output.log(\n isDeployable(plan)\n ? styleText('green', `\\nThis ${label} can be deployed.`)\n : styleText('red', `\\nThis ${label} can't be deployed.`),\n )\n\n renderIssues(output, 'Problems to fix:', problems)\n renderIssues(output, 'Warnings:', warnings)\n\n // A blocked deploy uploads nothing, so only list files for a deployable plan.\n if (isDeployable(plan) && plan.files.length > 0) {\n output.log(\n `\\nFiles to deploy (${plan.files.length} ${pluralize('file', plan.files.length)}, ${formatMB(totalBytes(plan.files))}):`,\n )\n for (const file of plan.files) {\n output.log(` ${file.path} (${formatMB(file.size)})`)\n }\n }\n}\n\nfunction formatMB(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`\n}\n"],"names":["readdir","stat","join","relative","sep","styleText","summarizeInterfaces","checkStatusIcon","nestLines","renderIssues","pluralize","listDeploymentFiles","sourceDir","fromDir","walk","dir","entries","withFileTypes","nested","Promise","all","map","entry","full","name","isDirectory","resolve","flat","absolute","files","file","path","split","size","toSorted","a","b","localeCompare","isDeployable","plan","checks","every","check","status","totalBytes","reduce","sum","deploymentPlanToJson","errors","warnings","message","solution","push","applicationType","type","applicationVersion","version","exposes","length","config","isSingleton","undefined","target","reportExposes","reporter","app","lines","index","report","renderDeploymentPlan","output","label","problems","filter","log","formatMB","bytes","toFixed"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,IAAI,QAAO,mBAAkB;AAC9C,SAAQC,IAAI,EAAEC,QAAQ,EAAEC,GAAG,QAAO,YAAW;AAC7C,SAAQC,SAAS,QAAO,YAAW;AAGnC,SAA6BC,mBAAmB,QAAO,+BAA8B;AAErF,SAAQC,eAAe,EAAEC,SAAS,EAAEC,YAAY,QAAO,uBAAsB;AAC7E,SAAQC,SAAS,QAAO,0BAAyB;AA2BjD;;;CAGC,GACD,OAAO,eAAeC,oBACpBC,SAAiB,EACjBC,OAAe;IAEf,MAAMC,OAAO,OAAOC;QAClB,IAAIC;QACJ,IAAI;YACFA,UAAU,MAAMhB,QAAQe,KAAK;gBAACE,eAAe;YAAI;QACnD,EAAE,OAAM;YACN,OAAO,EAAE;QACX;QACA,MAAMC,SAAS,MAAMC,QAAQC,GAAG,CAC9BJ,QAAQK,GAAG,CAAC,CAACC;YACX,MAAMC,OAAOrB,KAAKa,KAAKO,MAAME,IAAI;YACjC,OAAOF,MAAMG,WAAW,KAAKX,KAAKS,QAAQJ,QAAQO,OAAO,CAAC;gBAACH;aAAK;QAClE;QAEF,OAAOL,OAAOS,IAAI;IACpB;IAEA,MAAMC,WAAW,MAAMd,KAAKF;IAC5B,MAAMiB,QAAQ,MAAMV,QAAQC,GAAG,CAC7BQ,SAASP,GAAG,CAAC,OAAOS,OAAU,CAAA;YAC5B,8EAA8E;YAC9EC,MAAM5B,SAASU,SAASiB,MAAME,KAAK,CAAC5B,KAAKF,IAAI,CAAC;YAC9C+B,MAAM,AAAC,CAAA,MAAMhC,KAAK6B,KAAI,EAAGG,IAAI;QAC/B,CAAA;IAEF,OAAOJ,MAAMK,QAAQ,CAAC,CAACC,GAAGC,IAAMD,EAAEJ,IAAI,CAACM,aAAa,CAACD,EAAEL,IAAI;AAC7D;AAEA,OAAO,SAASO,aAAaC,IAAoB;IAC/C,OAAOA,KAAKC,MAAM,CAACC,KAAK,CAAC,CAACC,QAAUA,MAAMC,MAAM,KAAK;AACvD;AAEA,SAASC,WAAWf,KAAuB;IACzC,OAAOA,MAAMgB,MAAM,CAAC,CAACC,KAAKhB,OAASgB,MAAMhB,KAAKG,IAAI,EAAE;AACtD;AAEA;;;;CAIC,GACD,OAAO,SAASc,qBAAqBR,IAAoB;IAavD,MAAMS,SAAwC,CAAC;IAC/C,MAAMC,WAAqB,EAAE;IAC7B,KAAK,MAAMP,SAASH,KAAKC,MAAM,CAAE;QAC/B,IAAIE,MAAMC,MAAM,KAAK,QAAQK,MAAM,CAACN,MAAMQ,OAAO,CAAC,GAAGR,MAAMS,QAAQ,IAAI;aAClE,IAAIT,MAAMC,MAAM,KAAK,QAAQM,SAASG,IAAI,CAACV,MAAMQ,OAAO;IAC/D;IAEA,kEAAkE;IAClE,qDAAqD;IACrD,OAAO;QACLG,iBAAiBd,KAAKe,IAAI;QAC1BC,oBAAoBhB,KAAKiB,OAAO;QAChCR;QACA,GAAIT,KAAKkB,OAAO,CAACC,MAAM,GAAG,IAAI;YAACD,SAASlB,KAAKkB,OAAO;QAAA,IAAI,CAAC,CAAC;QAC1D5B,OAAOU,KAAKV,KAAK;QACjB,GAAIU,KAAKoB,MAAM,GAAG;YAACA,QAAQpB,KAAKoB,MAAM;QAAA,IAAI,CAAC,CAAC;QAC5CrB,cAAcA,aAAaC;QAC3B,GAAIA,KAAKqB,WAAW,KAAKC,YAAY,CAAC,IAAI;YAACD,aAAarB,KAAKqB,WAAW;QAAA,CAAC;QACzEE,QAAQvB,KAAKuB,MAAM;QACnBlB,YAAYA,WAAWL,KAAKV,KAAK;QACjCoB;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASc,cACdC,QAA6B,EAC7BC,GAA8C;IAE9C,MAAM,EAACR,OAAO,EAAES,KAAK,EAAC,GAAG5D,oBAAoB2D;IAC7C,KAAK,MAAM,CAACE,OAAOjB,QAAQ,IAAIgB,MAAMlD,OAAO,GAAI;QAC9CgD,SAASI,MAAM,CAAC;YAACX,SAASU,UAAU,IAAIV,UAAUI;YAAWX;YAASP,QAAQ;QAAM;IACtF;IACA,OAAOc;AACT;AAEA,OAAO,SAASY,qBAAqB9B,IAAoB,EAAE+B,MAAc;IACvE,MAAMC,QAAQhC,KAAKe,IAAI,KAAK,YAAY,gBAAgB;IACxD,MAAMkB,WAAWjC,KAAKC,MAAM,CAACiC,MAAM,CAAC,CAAC/B,QAAUA,MAAMC,MAAM,KAAK;IAChE,MAAMM,WAAWV,KAAKC,MAAM,CAACiC,MAAM,CAAC,CAAC/B,QAAUA,MAAMC,MAAM,KAAK;IAEhE2B,OAAOI,GAAG,CAAC;IAEX,4EAA4E;IAC5E,KAAK,MAAMhC,SAASH,KAAKC,MAAM,CAAE;QAC/B,IAAIE,MAAMC,MAAM,KAAK,UAAUD,MAAMC,MAAM,KAAK,QAAQ;YACtD2B,OAAOI,GAAG,CAAClE,UAAU,CAAC,EAAE,EAAED,gBAAgBmC,MAAMC,MAAM,EAAE,CAAC,EAAED,MAAMQ,OAAO,EAAE;QAC5E;IACF;IAEAoB,OAAOI,GAAG,CACRpC,aAAaC,QACTlC,UAAU,SAAS,CAAC,OAAO,EAAEkE,MAAM,iBAAiB,CAAC,IACrDlE,UAAU,OAAO,CAAC,OAAO,EAAEkE,MAAM,mBAAmB,CAAC;IAG3D9D,aAAa6D,QAAQ,oBAAoBE;IACzC/D,aAAa6D,QAAQ,aAAarB;IAElC,8EAA8E;IAC9E,IAAIX,aAAaC,SAASA,KAAKV,KAAK,CAAC6B,MAAM,GAAG,GAAG;QAC/CY,OAAOI,GAAG,CACR,CAAC,mBAAmB,EAAEnC,KAAKV,KAAK,CAAC6B,MAAM,CAAC,CAAC,EAAEhD,UAAU,QAAQ6B,KAAKV,KAAK,CAAC6B,MAAM,EAAE,EAAE,EAAEiB,SAAS/B,WAAWL,KAAKV,KAAK,GAAG,EAAE,CAAC;QAE1H,KAAK,MAAMC,QAAQS,KAAKV,KAAK,CAAE;YAC7ByC,OAAOI,GAAG,CAAC,CAAC,EAAE,EAAE5C,KAAKC,IAAI,CAAC,EAAE,EAAE4C,SAAS7C,KAAKG,IAAI,EAAE,CAAC,CAAC;QACtD;IACF;AACF;AAEA,SAAS0C,SAASC,KAAa;IAC7B,OAAO,GAAG,AAACA,CAAAA,QAAQ,OAAO,IAAG,EAAGC,OAAO,CAAC,GAAG,GAAG,CAAC;AACjD"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/deploymentPlan.ts"],"sourcesContent":["import {readdir, stat} from 'node:fs/promises'\nimport {join, relative, sep} from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\nimport {type DeployedInterface, summarizeInterfaces} from '@sanity/workbench-cli/deploy'\n\nimport {checkStatusIcon, nestLines, renderIssues} from '../../util/checks.js'\nimport {pluralize} from '../../util/pluralize.js'\nimport {type DeployCheck, type DeployCheckReporter, type DeployTarget} from './deployChecks.js'\nimport {type DeployPayload} from './deployRunner.js'\n\nexport interface DeploymentFile {\n /** Path relative to the project root, POSIX-style. */\n path: string\n size: number\n}\n\n/** What a `--dry-run` deploy would do: the real deploy sequence with every mutation gated off. */\nexport interface DeploymentPlan {\n checks: DeployCheck[]\n files: DeploymentFile[]\n /** `null` when the run bailed before collecting it. */\n payload: DeployPayload | null\n /** The resolved deploy target; `null` when the checks can't determine one. */\n target: DeployTarget | null\n type: 'coreApp' | 'studio'\n}\n\n/**\n * Lists the files a deploy would pack from `sourceDir`, as paths relative to\n * `fromDir`. A missing directory yields an empty list rather than throwing.\n */\nexport async function listDeploymentFiles(\n sourceDir: string,\n fromDir: string,\n): Promise<DeploymentFile[]> {\n const walk = async (dir: string): Promise<string[]> => {\n let entries\n try {\n entries = await readdir(dir, {withFileTypes: true})\n } catch {\n return []\n }\n const nested = await Promise.all(\n entries.map((entry) => {\n const full = join(dir, entry.name)\n return entry.isDirectory() ? walk(full) : Promise.resolve([full])\n }),\n )\n return nested.flat()\n }\n\n const absolute = await walk(sourceDir)\n const files = await Promise.all(\n absolute.map(async (file) => ({\n // Deploy paths are POSIX-style regardless of the host OS (Windows gives `\\`).\n path: relative(fromDir, file).split(sep).join('/'),\n size: (await stat(file)).size,\n })),\n )\n return files.toSorted((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function isDeployable(plan: DeploymentPlan): boolean {\n return plan.checks.every((check) => check.status !== 'fail')\n}\n\nfunction totalBytes(files: DeploymentFile[]): number {\n return files.reduce((sum, file) => sum + file.size, 0)\n}\n\n/**\n * A problem-focused, machine-readable projection of the plan: blocking problems\n * mapped to their fix, warnings as messages. Derived from the same checks the\n * human report renders (its pass/skip lines are informational and omitted here).\n */\nexport function deploymentPlanToJson(plan: DeploymentPlan): {\n action: DeployTarget['action'] | null\n application: null\n canDeploy: boolean\n errors: Record<string, string | null>\n files: DeploymentFile[]\n payload: DeployPayload | null\n reason: string | null\n totalBytes: number\n url: string | null\n warnings: string[]\n} {\n const errors: Record<string, string | null> = {}\n const warnings: string[] = []\n for (const check of plan.checks) {\n if (check.status === 'fail') errors[check.message] = check.solution ?? null\n else if (check.status === 'warn') warnings.push(check.message)\n }\n\n return {\n action: plan.target?.action ?? null,\n // Only a successful deploy reports the backend's record.\n application: null,\n canDeploy: isDeployable(plan),\n errors,\n files: plan.files,\n payload: plan.payload,\n reason: plan.checks.find((check) => check.status === 'fail')?.message ?? null,\n totalBytes: totalBytes(plan.files),\n url: plan.target?.url ?? null,\n warnings,\n }\n}\n\nexport function reportInterfaces(\n reporter: DeployCheckReporter,\n app: Parameters<typeof summarizeInterfaces>[0],\n): {services: DeployedInterface[]; views: DeployedInterface[]} {\n const {lines, services, views} = summarizeInterfaces(app)\n for (const message of lines) reporter.report({message, status: 'pass'})\n return {services, views}\n}\n\nexport function renderDeploymentPlan(plan: DeploymentPlan, output: Output): void {\n const label = plan.type === 'coreApp' ? 'application' : 'studio'\n const problems = plan.checks.filter((check) => check.status === 'fail')\n const warnings = plan.checks.filter((check) => check.status === 'warn')\n\n output.log('\\nDry run — no changes made.\\n')\n\n // Only pass/skip here; problems and warnings render below with their fixes.\n for (const check of plan.checks) {\n if (check.status === 'pass' || check.status === 'skip') {\n output.log(nestLines(` ${checkStatusIcon(check.status)} ${check.message}`))\n }\n }\n\n output.log(\n isDeployable(plan)\n ? styleText('green', `\\nThis ${label} can be deployed.`)\n : styleText('red', `\\nThis ${label} can't be deployed.`),\n )\n\n renderIssues(output, 'Problems to fix:', problems)\n renderIssues(output, 'Warnings:', warnings)\n\n // A blocked deploy uploads nothing, so only list files for a deployable plan.\n if (isDeployable(plan) && plan.files.length > 0) {\n output.log(\n `\\nFiles to deploy (${plan.files.length} ${pluralize('file', plan.files.length)}, ${formatMB(totalBytes(plan.files))}):`,\n )\n for (const file of plan.files) {\n output.log(` ${file.path} (${formatMB(file.size)})`)\n }\n }\n}\n\nfunction formatMB(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`\n}\n"],"names":["readdir","stat","join","relative","sep","styleText","summarizeInterfaces","checkStatusIcon","nestLines","renderIssues","pluralize","listDeploymentFiles","sourceDir","fromDir","walk","dir","entries","withFileTypes","nested","Promise","all","map","entry","full","name","isDirectory","resolve","flat","absolute","files","file","path","split","size","toSorted","a","b","localeCompare","isDeployable","plan","checks","every","check","status","totalBytes","reduce","sum","deploymentPlanToJson","errors","warnings","message","solution","push","action","target","application","canDeploy","payload","reason","find","url","reportInterfaces","reporter","app","lines","services","views","report","renderDeploymentPlan","output","label","type","problems","filter","log","length","formatMB","bytes","toFixed"],"mappings":"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"}
|
|
@@ -152,10 +152,7 @@ import { normalizeUrl, validateUrl } from './urlUtils.js';
|
|
|
152
152
|
return {
|
|
153
153
|
appHost: slug,
|
|
154
154
|
existing: {
|
|
155
|
-
|
|
156
|
-
id: existing.id,
|
|
157
|
-
organizationId: existing.organizationId,
|
|
158
|
-
title: existing.title,
|
|
155
|
+
...existing,
|
|
159
156
|
url: getApplicationUrl(existing)
|
|
160
157
|
},
|
|
161
158
|
type: 'slug-taken'
|
|
@@ -182,10 +179,7 @@ async function resolveAppById(appId) {
|
|
|
182
179
|
const application = await getApplication(appId);
|
|
183
180
|
return application ? {
|
|
184
181
|
application: {
|
|
185
|
-
|
|
186
|
-
id: application.id,
|
|
187
|
-
organizationId: application.organizationId,
|
|
188
|
-
title: application.title,
|
|
182
|
+
...application,
|
|
189
183
|
url: getApplicationUrl(application)
|
|
190
184
|
},
|
|
191
185
|
type: 'found'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/resolveDeployTarget.ts"],"sourcesContent":["import {getApplication, getApplicationUrl, listApplications} from '@sanity/workbench-cli/deploy'\n\nimport {\n getUserApplication,\n getUserApplications,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {APP_ID_NOT_FOUND_IN_ORGANIZATION} from '../../util/errorMessages.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\n/** The application fields a deploy-target verdict carries for reporting. */\nexport interface DeployTargetApp {\n appHost: string\n id: string\n title: string | null\n\n url?: string\n}\n\n/** A coreApp verdict also carries the organization, for the dashboard URL. */\ninterface DeployTargetCoreApp extends DeployTargetApp {\n organizationId: string\n}\n\n/**\n * The read-only outcome of resolving where a deploy would go.\n *\n * - `found` — the deploy targets this existing application\n * - `would-create` — nothing registered yet; a deploy would create it without prompting\n * - `needs-input` — config doesn't determine a target; a deploy would prompt\n * (`existing` lists the applications a prompt would offer)\n * - `invalid` — the configured target can never resolve (bad host, unknown appId)\n * - `blocked` — resolution requires config that's missing (projectId/organizationId)\n *\n * Transport errors (network, permissions) throw — they're not verdicts.\n *\n * The user-applications resolvers carry the full {@link UserApplication} (the\n * real deploy needs it); the workbench resolvers and the report only need\n * {@link DeployTargetApp}, so `App` defaults to that widened shape.\n */\ntype CommonDeployTargetResolution<App = DeployTargetApp> =\n | {application: App; type: 'found'}\n | {existing: App[]; type: 'needs-input'}\n | {message: string; reason: 'app-not-found' | 'invalid-host'; type: 'invalid'}\n | {message: string; type: 'blocked'}\n\nexport type StudioDeployTargetResolution<App = DeployTargetApp> =\n | CommonDeployTargetResolution<App>\n | {appHost: string; existing: App; type: 'slug-taken'}\n | {appHost: string; type: 'would-create'}\n\nexport type AppDeployTargetResolution<App = DeployTargetCoreApp> =\n | CommonDeployTargetResolution<App>\n | {appHost: string; existing: App; type: 'slug-taken'}\n | {appHost?: string; type: 'would-create'}\n\n/**\n * Owns the studio deploy-target rules: the --url flag over studioHost config,\n * appId over appHost precedence, external URL normalization and validation.\n * Both the real deploy and the dry run consume these verdicts.\n *\n * @internal\n */\nexport async function resolveStudioDeployTarget(options: {\n appId: string | undefined\n isExternal: boolean\n projectId: string | undefined\n studioHost: string | undefined\n urlFlag: string | undefined\n}): Promise<StudioDeployTargetResolution<UserApplication>> {\n const {appId, isExternal, projectId, studioHost, urlFlag} = options\n\n // appId wins over host config (and undeploy resolves it the same way): a\n // configured appId deploys even when studioHost is stale or invalid, so it's\n // resolved before any host validation.\n if (appId) {\n if (!projectId) {\n return {message: 'api.projectId is missing', type: 'blocked'}\n }\n const application = await getUserApplication({appId, isSdkApp: false, projectId})\n if (application) {\n return {application, type: 'found'}\n }\n return {\n message: `Cannot find app with app ID ${appId}`,\n reason: 'app-not-found',\n type: 'invalid',\n }\n }\n\n const {error: hostError, host: resolvedHost} = resolveAppHost({\n isExternal,\n studioHost,\n url: urlFlag,\n })\n if (hostError) {\n return {message: hostError, reason: 'invalid-host', type: 'invalid'}\n }\n\n // A host from config hasn't passed through the --url validation yet\n let appHost = resolvedHost\n if (appHost && isExternal) {\n appHost = normalizeUrl(appHost)\n const validation = validateUrl(appHost)\n if (validation !== true) {\n return {message: validation, reason: 'invalid-host', type: 'invalid'}\n }\n }\n\n if (appHost) {\n if (!projectId) {\n return {message: 'api.projectId is missing', type: 'blocked'}\n }\n const application = await getUserApplication({appHost, isSdkApp: false, projectId})\n if (application) {\n return {application, type: 'found'}\n }\n return {appHost, type: 'would-create'}\n }\n\n // Neither appId nor host configured — a deploy would prompt.\n // Without a project there is nothing to list; a deploy would still prompt.\n const existing = projectId ? await listStudioApplications(projectId, isExternal) : []\n return {existing, type: 'needs-input'}\n}\n\n/**\n * Owns the app deploy-target rules: appId lookup, falling back to listing the\n * organization's applications. Both the real deploy and the dry run consume\n * these verdicts.\n *\n * @internal\n */\nexport async function resolveAppDeployTarget(options: {\n appId: string | undefined\n organizationId: string | undefined\n}): Promise<AppDeployTargetResolution<UserApplicationResolved>> {\n const {appId, organizationId} = options\n\n if (appId) {\n const application = await getUserApplication({appId, isSdkApp: true})\n if (application) {\n return {application, type: 'found'}\n }\n return {\n message: `Cannot find app with app ID ${appId}`,\n reason: 'app-not-found',\n type: 'invalid',\n }\n }\n\n if (!organizationId) {\n return {message: 'app.organizationId is missing', type: 'blocked'}\n }\n\n const existing = await getUserApplications({appType: 'coreApp', organizationId})\n if (existing?.length) {\n return {existing, type: 'needs-input'}\n }\n\n return {type: 'would-create'}\n}\n\n/**\n * The dry-run counterpart to a workbench app's create-on-deploy: without an\n * `appId`, an org already holding the slug resolves to that app, so the report\n * can name its id instead of a create that would fail.\n *\n * @internal\n */\nexport async function resolveWorkbenchApp({\n appId,\n organizationId,\n slug,\n}: {\n appId: string | undefined\n organizationId?: string\n slug?: string\n}): Promise<AppDeployTargetResolution> {\n if (appId) return resolveAppById(appId)\n\n if (organizationId && slug) {\n const existing = (await listApplications(organizationId)).find((app) => app.slug === slug)\n if (existing) {\n return {\n appHost: slug,\n existing: {\n appHost: slug,\n id: existing.id,\n organizationId: existing.organizationId,\n title: existing.title,\n url: getApplicationUrl(existing),\n },\n type: 'slug-taken',\n }\n }\n }\n\n return {appHost: slug, type: 'would-create'}\n}\n\n/**\n * {@link resolveWorkbenchApp} narrowed for a studio, whose slug is always configured.\n *\n * @internal\n */\nexport async function resolveWorkbenchStudio(options: {\n appId: string | undefined\n organizationId?: string\n slug: string\n}): Promise<StudioDeployTargetResolution> {\n const resolution = await resolveWorkbenchApp(options)\n return resolution.type === 'would-create'\n ? {appHost: options.slug, type: 'would-create'}\n : resolution\n}\n\nasync function resolveAppById(\n appId: string,\n): Promise<CommonDeployTargetResolution<DeployTargetCoreApp>> {\n const application = await getApplication(appId)\n return application\n ? {\n application: {\n appHost: application.slug ?? '',\n id: application.id,\n organizationId: application.organizationId,\n title: application.title,\n url: getApplicationUrl(application),\n },\n type: 'found',\n }\n : {message: APP_ID_NOT_FOUND_IN_ORGANIZATION, reason: 'app-not-found', type: 'invalid'}\n}\n\nasync function listStudioApplications(\n projectId: string,\n isExternal: boolean,\n): Promise<UserApplication[]> {\n const urlType = isExternal ? 'external' : 'internal'\n const applications = await getUserApplications({appType: 'studio', projectId})\n // External deploys should only see external studios and vice versa\n return applications?.filter((application) => application.urlType === urlType) ?? []\n}\n\nfunction resolveAppHost({\n isExternal,\n studioHost,\n url,\n}: {\n isExternal: boolean\n studioHost: string | undefined\n url: string | undefined\n}): {error?: string; host?: string} {\n if (!url) {\n return {host: studioHost}\n }\n\n if (isExternal) {\n const normalized = normalizeUrl(url)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return {error: validation}\n }\n return {host: normalized}\n }\n\n // For internal deploys, strip protocol prefix and .sanity.studio suffix if present\n const hostname = url.replace(/^https?:\\/\\//i, '').replace(/\\.sanity\\.studio\\/?$/i, '')\n\n // If the result still looks like a URL (contains dots), the user likely meant --external\n if (hostname.includes('.')) {\n return {\n error: `\"${hostname}\" does not look like a sanity.studio hostname. Did you mean to use --external?`,\n }\n }\n\n // Validate hostname characters (alphanumeric and hyphens only)\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(hostname)) {\n return {\n error: `Invalid studio hostname \"${hostname}\". Hostnames can only contain letters, numbers, and hyphens.`,\n }\n }\n\n return {host: hostname}\n}\n"],"names":["getApplication","getApplicationUrl","listApplications","getUserApplication","getUserApplications","APP_ID_NOT_FOUND_IN_ORGANIZATION","normalizeUrl","validateUrl","resolveStudioDeployTarget","options","appId","isExternal","projectId","studioHost","urlFlag","message","type","application","isSdkApp","reason","error","hostError","host","resolvedHost","resolveAppHost","url","appHost","validation","existing","listStudioApplications","resolveAppDeployTarget","organizationId","appType","length","resolveWorkbenchApp","slug","resolveAppById","find","app","id","title","resolveWorkbenchStudio","resolution","urlType","applications","filter","normalized","hostname","replace","includes","test"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/resolveDeployTarget.ts"],"sourcesContent":["import {getApplication, getApplicationUrl, listApplications} from '@sanity/workbench-cli/deploy'\n\nimport {\n getUserApplication,\n getUserApplications,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {APP_ID_NOT_FOUND_IN_ORGANIZATION} from '../../util/errorMessages.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\n/** The application fields a deploy-target verdict carries for reporting. */\nexport interface DeployTargetApp {\n id: string\n title: string | null\n type: 'coreApp' | 'studio'\n\n appHost?: string\n url?: string\n}\n\n/** A coreApp verdict also carries the organization, for the dashboard URL. */\ninterface DeployTargetCoreApp extends DeployTargetApp {\n organizationId: string\n}\n\n/**\n * The read-only outcome of resolving where a deploy would go.\n *\n * - `found` — the deploy targets this existing application\n * - `would-create` — nothing registered yet; a deploy would create it without prompting\n * - `needs-input` — config doesn't determine a target; a deploy would prompt\n * (`existing` lists the applications a prompt would offer)\n * - `invalid` — the configured target can never resolve (bad host, unknown appId)\n * - `blocked` — resolution requires config that's missing (projectId/organizationId)\n *\n * Transport errors (network, permissions) throw — they're not verdicts.\n *\n * The user-applications resolvers carry the full {@link UserApplication} (the\n * real deploy needs it); the workbench resolvers and the report only need\n * {@link DeployTargetApp}, so `App` defaults to that widened shape.\n */\ntype CommonDeployTargetResolution<App = DeployTargetApp> =\n | {application: App; type: 'found'}\n | {existing: App[]; type: 'needs-input'}\n | {message: string; reason: 'app-not-found' | 'invalid-host'; type: 'invalid'}\n | {message: string; type: 'blocked'}\n\nexport type StudioDeployTargetResolution<App = DeployTargetApp> =\n | CommonDeployTargetResolution<App>\n | {appHost: string; existing: App; type: 'slug-taken'}\n | {appHost: string; type: 'would-create'}\n\nexport type AppDeployTargetResolution<App = DeployTargetCoreApp> =\n | CommonDeployTargetResolution<App>\n | {appHost: string; existing: App; type: 'slug-taken'}\n | {appHost?: string; type: 'would-create'}\n\n/**\n * Owns the studio deploy-target rules: the --url flag over studioHost config,\n * appId over appHost precedence, external URL normalization and validation.\n * Both the real deploy and the dry run consume these verdicts.\n *\n * @internal\n */\nexport async function resolveStudioDeployTarget(options: {\n appId: string | undefined\n isExternal: boolean\n projectId: string | undefined\n studioHost: string | undefined\n urlFlag: string | undefined\n}): Promise<StudioDeployTargetResolution<UserApplication>> {\n const {appId, isExternal, projectId, studioHost, urlFlag} = options\n\n // appId wins over host config (and undeploy resolves it the same way): a\n // configured appId deploys even when studioHost is stale or invalid, so it's\n // resolved before any host validation.\n if (appId) {\n if (!projectId) {\n return {message: 'api.projectId is missing', type: 'blocked'}\n }\n const application = await getUserApplication({appId, isSdkApp: false, projectId})\n if (application) {\n return {application, type: 'found'}\n }\n return {\n message: `Cannot find app with app ID ${appId}`,\n reason: 'app-not-found',\n type: 'invalid',\n }\n }\n\n const {error: hostError, host: resolvedHost} = resolveAppHost({\n isExternal,\n studioHost,\n url: urlFlag,\n })\n if (hostError) {\n return {message: hostError, reason: 'invalid-host', type: 'invalid'}\n }\n\n // A host from config hasn't passed through the --url validation yet\n let appHost = resolvedHost\n if (appHost && isExternal) {\n appHost = normalizeUrl(appHost)\n const validation = validateUrl(appHost)\n if (validation !== true) {\n return {message: validation, reason: 'invalid-host', type: 'invalid'}\n }\n }\n\n if (appHost) {\n if (!projectId) {\n return {message: 'api.projectId is missing', type: 'blocked'}\n }\n const application = await getUserApplication({appHost, isSdkApp: false, projectId})\n if (application) {\n return {application, type: 'found'}\n }\n return {appHost, type: 'would-create'}\n }\n\n // Neither appId nor host configured — a deploy would prompt.\n // Without a project there is nothing to list; a deploy would still prompt.\n const existing = projectId ? await listStudioApplications(projectId, isExternal) : []\n return {existing, type: 'needs-input'}\n}\n\n/**\n * Owns the app deploy-target rules: appId lookup, falling back to listing the\n * organization's applications. Both the real deploy and the dry run consume\n * these verdicts.\n *\n * @internal\n */\nexport async function resolveAppDeployTarget(options: {\n appId: string | undefined\n organizationId: string | undefined\n}): Promise<AppDeployTargetResolution<UserApplicationResolved>> {\n const {appId, organizationId} = options\n\n if (appId) {\n const application = await getUserApplication({appId, isSdkApp: true})\n if (application) {\n return {application, type: 'found'}\n }\n return {\n message: `Cannot find app with app ID ${appId}`,\n reason: 'app-not-found',\n type: 'invalid',\n }\n }\n\n if (!organizationId) {\n return {message: 'app.organizationId is missing', type: 'blocked'}\n }\n\n const existing = await getUserApplications({appType: 'coreApp', organizationId})\n if (existing?.length) {\n return {existing, type: 'needs-input'}\n }\n\n return {type: 'would-create'}\n}\n\n/**\n * The dry-run counterpart to a workbench app's create-on-deploy: without an\n * `appId`, an org already holding the slug resolves to that app, so the report\n * can name its id instead of a create that would fail.\n *\n * @internal\n */\nexport async function resolveWorkbenchApp({\n appId,\n organizationId,\n slug,\n}: {\n appId: string | undefined\n organizationId?: string\n slug?: string\n}): Promise<AppDeployTargetResolution> {\n if (appId) return resolveAppById(appId)\n\n if (organizationId && slug) {\n const existing = (await listApplications(organizationId)).find((app) => app.slug === slug)\n if (existing) {\n return {\n appHost: slug,\n existing: {...existing, url: getApplicationUrl(existing)},\n type: 'slug-taken',\n }\n }\n }\n\n return {appHost: slug, type: 'would-create'}\n}\n\n/**\n * {@link resolveWorkbenchApp} narrowed for a studio, whose slug is always configured.\n *\n * @internal\n */\nexport async function resolveWorkbenchStudio(options: {\n appId: string | undefined\n organizationId?: string\n slug: string\n}): Promise<StudioDeployTargetResolution> {\n const resolution = await resolveWorkbenchApp(options)\n return resolution.type === 'would-create'\n ? {appHost: options.slug, type: 'would-create'}\n : resolution\n}\n\nasync function resolveAppById(\n appId: string,\n): Promise<CommonDeployTargetResolution<DeployTargetCoreApp>> {\n const application = await getApplication(appId)\n return application\n ? {\n application: {...application, url: getApplicationUrl(application)},\n type: 'found',\n }\n : {message: APP_ID_NOT_FOUND_IN_ORGANIZATION, reason: 'app-not-found', type: 'invalid'}\n}\n\nasync function listStudioApplications(\n projectId: string,\n isExternal: boolean,\n): Promise<UserApplication[]> {\n const urlType = isExternal ? 'external' : 'internal'\n const applications = await getUserApplications({appType: 'studio', projectId})\n // External deploys should only see external studios and vice versa\n return applications?.filter((application) => application.urlType === urlType) ?? []\n}\n\nfunction resolveAppHost({\n isExternal,\n studioHost,\n url,\n}: {\n isExternal: boolean\n studioHost: string | undefined\n url: string | undefined\n}): {error?: string; host?: string} {\n if (!url) {\n return {host: studioHost}\n }\n\n if (isExternal) {\n const normalized = normalizeUrl(url)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return {error: validation}\n }\n return {host: normalized}\n }\n\n // For internal deploys, strip protocol prefix and .sanity.studio suffix if present\n const hostname = url.replace(/^https?:\\/\\//i, '').replace(/\\.sanity\\.studio\\/?$/i, '')\n\n // If the result still looks like a URL (contains dots), the user likely meant --external\n if (hostname.includes('.')) {\n return {\n error: `\"${hostname}\" does not look like a sanity.studio hostname. Did you mean to use --external?`,\n }\n }\n\n // Validate hostname characters (alphanumeric and hyphens only)\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(hostname)) {\n return {\n error: `Invalid studio hostname \"${hostname}\". Hostnames can only contain letters, numbers, and hyphens.`,\n }\n }\n\n return {host: hostname}\n}\n"],"names":["getApplication","getApplicationUrl","listApplications","getUserApplication","getUserApplications","APP_ID_NOT_FOUND_IN_ORGANIZATION","normalizeUrl","validateUrl","resolveStudioDeployTarget","options","appId","isExternal","projectId","studioHost","urlFlag","message","type","application","isSdkApp","reason","error","hostError","host","resolvedHost","resolveAppHost","url","appHost","validation","existing","listStudioApplications","resolveAppDeployTarget","organizationId","appType","length","resolveWorkbenchApp","slug","resolveAppById","find","app","resolveWorkbenchStudio","resolution","urlType","applications","filter","normalized","hostname","replace","includes","test"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { getSanityEnvVar } from '@sanity/cli-core';
|
|
3
3
|
import { logSymbols, spinner } from '@sanity/cli-core/ux';
|
|
4
|
-
import { isWorkbenchApp
|
|
4
|
+
import { isWorkbenchApp } from '@sanity/workbench-cli';
|
|
5
5
|
import { determineIsApp } from '../../../util/determineIsApp.js';
|
|
6
6
|
import { getSharedServerConfig } from '../../../util/getSharedServerConfig.js';
|
|
7
7
|
import { resolveReactStrictMode } from '../../../util/resolveReactStrictMode.js';
|
|
@@ -50,13 +50,7 @@ export function getDevServerConfig({ cliConfig, flags, httpPort, output, workDir
|
|
|
50
50
|
reactStrictMode,
|
|
51
51
|
staticPath: path.join(workDir, 'static'),
|
|
52
52
|
typegen: cliConfig?.typegen,
|
|
53
|
-
|
|
54
|
-
// matches how the workbench addresses it. The registry re-reads the bound
|
|
55
|
-
// port if a non-strict shift moves it.
|
|
56
|
-
workbenchAppId: isWorkbenchApp(app) ? resolveAppId({
|
|
57
|
-
host: baseConfig.httpHost,
|
|
58
|
-
port: httpPort ?? baseConfig.httpPort
|
|
59
|
-
}) : undefined
|
|
53
|
+
workbenchAppId: isWorkbenchApp(app) ? app.slug : undefined
|
|
60
54
|
};
|
|
61
55
|
}
|
|
62
56
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/dev/servers/getDevServerConfig.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type CliConfig, getSanityEnvVar, type Output} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {isWorkbenchApp
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/dev/servers/getDevServerConfig.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type CliConfig, getSanityEnvVar, type Output} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {isWorkbenchApp} from '@sanity/workbench-cli'\n\nimport {type DevServerOptions} from '../../../server/devServer.js'\nimport {determineIsApp} from '../../../util/determineIsApp.js'\nimport {getSharedServerConfig} from '../../../util/getSharedServerConfig.js'\nimport {resolveReactStrictMode} from '../../../util/resolveReactStrictMode.js'\nimport {type DevFlags} from '../types.js'\n\nexport function getDevServerConfig({\n cliConfig,\n flags,\n httpPort,\n output,\n workDir,\n}: {\n cliConfig?: CliConfig\n flags: DevFlags\n httpPort?: number\n output: Output\n workDir: string\n}): Omit<DevServerOptions, 'spinner'> {\n const configSpinner = spinner('Checking configuration files...')\n\n const baseConfig = getSharedServerConfig({\n cliConfig,\n flags: {\n host: flags.host,\n port: flags.port,\n },\n workDir,\n })\n\n configSpinner.succeed()\n\n const isApp = cliConfig ? determineIsApp(cliConfig) : false\n const reactStrictMode = resolveReactStrictMode(cliConfig)\n // `views`/`services` are declared via `unstable_defineApp`, so read them off\n // the branded app result rather than the legacy `app` config type.\n const app = cliConfig?.app\n\n const envBasePath = getSanityEnvVar('BASEPATH', isApp ?? false)\n if (envBasePath && cliConfig?.project?.basePath) {\n output.warn(\n `Overriding configured base path (${cliConfig.project.basePath}) with value from environment variable (${envBasePath})`,\n )\n }\n\n // Unstable opt-in to Vite's experimental bundled dev mode, via\n // `unstable_bundledDev` in sanity.cli.ts. Defaults to off.\n const bundledDev = cliConfig?.unstable_bundledDev ?? false\n if (bundledDev) {\n output.log(`${logSymbols.info} Running dev server with experimental Vite bundled dev mode`)\n }\n\n return {\n ...baseConfig,\n bundledDev,\n // The app's navigable entry. A branded app that omits `entry` has no app\n // view: the runtime/federation skip the `./App` render path entirely.\n entry: app?.entry,\n exposes: isWorkbenchApp(app)\n ? {config: app.config, services: app.services, views: app.views}\n : undefined,\n // `devAction` passes an explicit port when a running workbench claimed the\n // configured one; otherwise the shared resolution stands.\n httpPort: httpPort ?? baseConfig.httpPort,\n isWorkbenchApp: isWorkbenchApp(app),\n reactCompiler: cliConfig && 'reactCompiler' in cliConfig ? cliConfig.reactCompiler : undefined,\n reactStrictMode,\n staticPath: path.join(workDir, 'static'),\n typegen: cliConfig?.typegen,\n workbenchAppId: isWorkbenchApp(app) ? app.slug : undefined,\n }\n}\n"],"names":["path","getSanityEnvVar","logSymbols","spinner","isWorkbenchApp","determineIsApp","getSharedServerConfig","resolveReactStrictMode","getDevServerConfig","cliConfig","flags","httpPort","output","workDir","configSpinner","baseConfig","host","port","succeed","isApp","reactStrictMode","app","envBasePath","project","basePath","warn","bundledDev","unstable_bundledDev","log","info","entry","exposes","config","services","views","undefined","reactCompiler","staticPath","join","typegen","workbenchAppId","slug"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAE5B,SAAwBC,eAAe,QAAoB,mBAAkB;AAC7E,SAAQC,UAAU,EAAEC,OAAO,QAAO,sBAAqB;AACvD,SAAQC,cAAc,QAAO,wBAAuB;AAGpD,SAAQC,cAAc,QAAO,kCAAiC;AAC9D,SAAQC,qBAAqB,QAAO,yCAAwC;AAC5E,SAAQC,sBAAsB,QAAO,0CAAyC;AAG9E,OAAO,SAASC,mBAAmB,EACjCC,SAAS,EACTC,KAAK,EACLC,QAAQ,EACRC,MAAM,EACNC,OAAO,EAOR;IACC,MAAMC,gBAAgBX,QAAQ;IAE9B,MAAMY,aAAaT,sBAAsB;QACvCG;QACAC,OAAO;YACLM,MAAMN,MAAMM,IAAI;YAChBC,MAAMP,MAAMO,IAAI;QAClB;QACAJ;IACF;IAEAC,cAAcI,OAAO;IAErB,MAAMC,QAAQV,YAAYJ,eAAeI,aAAa;IACtD,MAAMW,kBAAkBb,uBAAuBE;IAC/C,6EAA6E;IAC7E,mEAAmE;IACnE,MAAMY,MAAMZ,WAAWY;IAEvB,MAAMC,cAAcrB,gBAAgB,YAAYkB,SAAS;IACzD,IAAIG,eAAeb,WAAWc,SAASC,UAAU;QAC/CZ,OAAOa,IAAI,CACT,CAAC,iCAAiC,EAAEhB,UAAUc,OAAO,CAACC,QAAQ,CAAC,wCAAwC,EAAEF,YAAY,CAAC,CAAC;IAE3H;IAEA,+DAA+D;IAC/D,2DAA2D;IAC3D,MAAMI,aAAajB,WAAWkB,uBAAuB;IACrD,IAAID,YAAY;QACdd,OAAOgB,GAAG,CAAC,GAAG1B,WAAW2B,IAAI,CAAC,2DAA2D,CAAC;IAC5F;IAEA,OAAO;QACL,GAAGd,UAAU;QACbW;QACA,yEAAyE;QACzE,sEAAsE;QACtEI,OAAOT,KAAKS;QACZC,SAAS3B,eAAeiB,OACpB;YAACW,QAAQX,IAAIW,MAAM;YAAEC,UAAUZ,IAAIY,QAAQ;YAAEC,OAAOb,IAAIa,KAAK;QAAA,IAC7DC;QACJ,2EAA2E;QAC3E,0DAA0D;QAC1DxB,UAAUA,YAAYI,WAAWJ,QAAQ;QACzCP,gBAAgBA,eAAeiB;QAC/Be,eAAe3B,aAAa,mBAAmBA,YAAYA,UAAU2B,aAAa,GAAGD;QACrFf;QACAiB,YAAYrC,KAAKsC,IAAI,CAACzB,SAAS;QAC/B0B,SAAS9B,WAAW8B;QACpBC,gBAAgBpC,eAAeiB,OAAOA,IAAIoB,IAAI,GAAGN;IACnD;AACF"}
|
|
@@ -10,6 +10,7 @@ import { getProjectById } from '../../../services/projects.js';
|
|
|
10
10
|
import { getAppId } from '../../../util/appId.js';
|
|
11
11
|
import { getPackageManagerChoice } from '../../../util/packageManager/packageManagerChoice.js';
|
|
12
12
|
import { upgradePackages } from '../../../util/packageManager/upgradePackages.js';
|
|
13
|
+
import { hasLocalUnclaimedProject } from '../../../util/unclaimedProjects.js';
|
|
13
14
|
import { checkDependenciesEventListenerFactory } from '../../build/eventListenerFactory.js';
|
|
14
15
|
import { shouldAutoUpdate } from '../../build/shouldAutoUpdate.js';
|
|
15
16
|
import { devDebug } from '../devDebug.js';
|
|
@@ -141,7 +142,7 @@ export async function startStudioDevServer(options) {
|
|
|
141
142
|
})));
|
|
142
143
|
} else {
|
|
143
144
|
const startupDuration = Date.now() - startTime;
|
|
144
|
-
const url = `http://${httpHost || 'localhost'}:${port}${config.basePath}
|
|
145
|
+
const url = withUnclaimedSignInHash(`http://${httpHost || 'localhost'}:${port}${config.basePath}`, projectId);
|
|
145
146
|
const appType = 'Sanity Studio';
|
|
146
147
|
const viteVersion = await getLocalPackageVersion('vite', import.meta.url);
|
|
147
148
|
spin.succeed();
|
|
@@ -157,5 +158,10 @@ export async function startStudioDevServer(options) {
|
|
|
157
158
|
throw gracefulServerDeath('dev', config.httpHost, config.httpPort, err);
|
|
158
159
|
}
|
|
159
160
|
}
|
|
161
|
+
function withUnclaimedSignInHash(url, projectId) {
|
|
162
|
+
const token = process.env.SANITY_AUTH_TOKEN;
|
|
163
|
+
if (!token || !projectId || !hasLocalUnclaimedProject(projectId)) return url;
|
|
164
|
+
return `${url}#token=${encodeURIComponent(token)}`;
|
|
165
|
+
}
|
|
160
166
|
|
|
161
167
|
//# sourceMappingURL=startStudioDevServer.js.map
|