@sanity/cli 7.7.1 → 7.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -2
- package/dist/actions/auth/ensureAuthenticated.js +1 -1
- package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
- package/dist/actions/auth/login/validateToken.js +1 -1
- package/dist/actions/auth/login/validateToken.js.map +1 -1
- package/dist/actions/build/buildApp.js +2 -3
- package/dist/actions/build/buildApp.js.map +1 -1
- package/dist/actions/build/buildStudio.js +1 -2
- package/dist/actions/build/buildStudio.js.map +1 -1
- package/dist/actions/deploy/createUserApplication.js +8 -8
- package/dist/actions/deploy/createUserApplication.js.map +1 -1
- package/dist/actions/deploy/deployApp.js +128 -26
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployChecks.js +97 -72
- package/dist/actions/deploy/deployChecks.js.map +1 -1
- package/dist/actions/deploy/deployRunner.js +8 -8
- package/dist/actions/deploy/deployRunner.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +129 -23
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/deploymentPlan.js +29 -30
- package/dist/actions/deploy/deploymentPlan.js.map +1 -1
- package/dist/actions/deploy/findUserApplication.js +39 -14
- package/dist/actions/deploy/findUserApplication.js.map +1 -1
- package/dist/actions/deploy/resolveDeployTarget.js +44 -0
- package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
- package/dist/actions/deploy/urlUtils.js +0 -4
- package/dist/actions/deploy/urlUtils.js.map +1 -1
- package/dist/actions/dev/servers/getDevServerConfig.js +1 -1
- package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
- package/dist/actions/dev/servers/startStudioDevServer.js +1 -2
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
- package/dist/actions/documents/types.js.map +1 -1
- package/dist/actions/documents/validate.js +2 -1
- package/dist/actions/documents/validate.js.map +1 -1
- package/dist/actions/graphql/SchemaError.js.map +1 -1
- package/dist/actions/graphql/extractGraphQLAPIs.js +3 -1
- package/dist/actions/graphql/extractGraphQLAPIs.js.map +1 -1
- package/dist/actions/manifest/extractCoreAppManifest.js +1 -1
- package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
- package/dist/actions/organizations/getOrganization.js +1 -1
- package/dist/actions/organizations/getOrganization.js.map +1 -1
- package/dist/actions/schema/validateAction.js +1 -1
- package/dist/actions/schema/validateAction.js.map +1 -1
- package/dist/actions/skills/setupSkills.js +1 -1
- package/dist/actions/skills/setupSkills.js.map +1 -1
- package/dist/actions/undeploy/adapters.js +101 -0
- package/dist/actions/undeploy/adapters.js.map +1 -0
- package/dist/actions/undeploy/runUndeploy.js +179 -0
- package/dist/actions/undeploy/runUndeploy.js.map +1 -0
- package/dist/actions/undeploy/undeployPlan.js +123 -0
- package/dist/actions/undeploy/undeployPlan.js.map +1 -0
- package/dist/commands/datasets/copy.js +2 -1
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/documents/validate.js +18 -17
- package/dist/commands/documents/validate.js.map +1 -1
- package/dist/commands/mcp/configure.js +1 -1
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/projects/create.js +18 -13
- package/dist/commands/projects/create.js.map +1 -1
- package/dist/commands/projects/list.js +2 -1
- package/dist/commands/projects/list.js.map +1 -1
- package/dist/commands/schemas/validate.js +3 -2
- package/dist/commands/schemas/validate.js.map +1 -1
- package/dist/commands/skills/install.js +1 -1
- package/dist/commands/skills/install.js.map +1 -1
- package/dist/commands/tokens/list.js +1 -1
- package/dist/commands/tokens/list.js.map +1 -1
- package/dist/commands/undeploy.js +47 -73
- package/dist/commands/undeploy.js.map +1 -1
- package/dist/server/devServer.js.map +1 -1
- package/dist/util/checks.js +83 -0
- package/dist/util/checks.js.map +1 -0
- package/dist/util/toStderrOutput.js +13 -0
- package/dist/util/toStderrOutput.js.map +1 -0
- package/oclif.manifest.json +28 -1
- package/package.json +7 -8
- package/dist/actions/undeploy/getStudioOrAppUserApplication.js +0 -36
- package/dist/actions/undeploy/getStudioOrAppUserApplication.js.map +0 -1
- package/dist/util/compareDependencyVersions.js +0 -110
- package/dist/util/compareDependencyVersions.js.map +0 -1
- package/dist/util/getErrorMessage.js +0 -28
- package/dist/util/getErrorMessage.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/findUserApplication.ts"],"sourcesContent":["/**\n * Finds (or creates) the user application a deploy targets — the interactive\n * adapter that turns the resolveDeployTarget verdicts into prompts, creation,\n * and exits. Dry runs consume the same verdicts read-only (see deployChecks).\n */\n\nimport {type CliConfig, type Output} from '@sanity/cli-core'\nimport {select, Separator, spinner} from '@sanity/cli-core/ux'\n\nimport {\n createUserApplication,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {getErrorMessage} from '../../util/getErrorMessage.js'\nimport {\n createFailFastReporter,\n describeAppTarget,\n describeAppTargetError,\n describeStudioTarget,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {resolveAppDeployTarget, resolveStudioDeployTarget} from './resolveDeployTarget.js'\n\ninterface FindUserApplicationOptions {\n cliConfig: CliConfig\n organizationId: string\n output: Output\n\n title?: string\n unattended?: boolean\n}\n\nexport async function findUserApplication(\n options: FindUserApplicationOptions,\n): Promise<UserApplicationResolved | null> {\n const {cliConfig, organizationId, output, title, unattended = false} = options\n const spin = spinner('Checking application info...').start()\n\n let resolution\n try {\n resolution = await resolveAppDeployTarget({appId: getAppId(cliConfig), organizationId})\n deployDebug('Resolved app deploy target', resolution)\n } catch (error) {\n spin.clear()\n deployDebug('Error finding user application for app', error)\n output.error(describeAppTargetError(error, organizationId), {exit: 1})\n return null\n }\n\n if (resolution.type === 'found') {\n spin.succeed()\n return resolution.application\n }\n\n // null tells the caller to create. Unattended runs can only create with a\n // --title; picking among existing apps (needs-input) always needs a prompt.\n if (resolution.type === 'would-create' && (!unattended || title)) {\n spin.info('No application ID configured')\n return null\n }\n if (resolution.type === 'needs-input' && !unattended) {\n spin.info('No application ID configured')\n return promptForExistingApp(resolution.existing)\n }\n\n spin.clear()\n // 'blocked' diagnoses as a skip (its root cause fails an earlier check), so it\n // needs an explicit exit here to not fall through to application creation\n if (resolution.type === 'blocked') {\n output.error(resolution.message, {exit: 1})\n return null\n }\n createFailFastReporter(output).report(describeAppTarget(resolution))\n return null\n}\n\ninterface FindUserApplicationForStudioOptions {\n isExternal: boolean\n output: Output\n projectId: string\n\n appId?: string\n studioHost?: string\n title?: string\n unattended?: boolean\n urlFlag?: string\n}\n\nexport async function findUserApplicationForStudio(\n options: FindUserApplicationForStudioOptions,\n): Promise<UserApplication | null> {\n const {\n appId,\n isExternal,\n output,\n projectId,\n studioHost,\n title,\n unattended = false,\n urlFlag,\n } = options\n const urlType = isExternal ? 'external' : 'internal'\n const spin = spinner('Checking project info').start()\n\n let resolution\n try {\n resolution = await resolveStudioDeployTarget({\n appId,\n isExternal,\n projectId,\n studioHost,\n urlFlag,\n })\n deployDebug('Resolved studio deploy target', resolution)\n } catch (error) {\n spin.fail()\n deployDebug('Error finding user application', error)\n output.error(`Failed to resolve deploy target: ${getErrorMessage(error)}`, {exit: 1})\n return null\n }\n\n if (resolution.type === 'found') {\n spin.succeed()\n return resolution.application\n }\n\n // The configured host isn't registered yet — a deploy registers it without prompting\n if (resolution.type === 'would-create') {\n spin.succeed()\n return createFromConfiguredHost({\n appHost: resolution.appHost,\n output,\n projectId,\n title,\n urlType,\n })\n }\n\n if (resolution.type === 'needs-input' && !unattended) {\n spin.succeed()\n // Nothing to select from — the caller prompts for a brand new host\n if (resolution.existing.length === 0) return null\n return promptForExistingStudio({existing: resolution.existing, urlType})\n }\n\n spin.fail()\n // 'blocked' diagnoses as a skip (its root cause fails an earlier check), so it\n // needs an explicit exit here\n if (resolution.type === 'blocked') {\n output.error(resolution.message, {exit: 1})\n return null\n }\n createFailFastReporter(output).report(describeStudioTarget(resolution, {isExternal}))\n return null\n}\n\n/**\n * The host is configured (studioHost or --url) but not registered yet:\n * a deploy registers it without prompting.\n */\nasync function createFromConfiguredHost({\n appHost,\n output,\n projectId,\n title,\n urlType,\n}: {\n appHost: string\n output: Output\n projectId: string\n title?: string\n urlType: 'external' | 'internal'\n}): Promise<UserApplication | null> {\n if (urlType === 'external') {\n output.log('Your project has not been registered with an external studio URL.')\n output.log(`Registering ${appHost}`)\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log(`Creating https://${appHost}.sanity.studio`)\n }\n output.log('')\n\n const spin = spinner(\n urlType === 'external' ? 'Registering external studio' : 'Creating studio hostname',\n ).start()\n\n try {\n const response = await createUserApplication({\n appType: 'studio',\n body: {appHost, title, type: 'studio', urlType},\n projectId,\n })\n spin.succeed()\n return response\n } catch (e) {\n spin.fail()\n // if the name is taken, it should return a 409 so we relay to the user\n if ([402, 409].includes(e?.statusCode)) {\n output.error(e?.response?.body?.message || 'Bad request', {exit: 1})\n return null\n }\n // otherwise, it's a fatal error\n deployDebug('Error creating user application from config', e)\n output.error(\n `Error creating user application from config: ${e instanceof Error ? e.message : e}`,\n {exit: 1},\n )\n return null\n }\n}\n\nasync function promptForExistingApp(\n existing: UserApplicationResolved[],\n): Promise<UserApplicationResolved | null> {\n const choices = existing.map((app) => ({name: app.title ?? app.appHost, value: app.appHost}))\n\n const selected = await select({\n choices: [\n {name: 'New application deployment', value: 'NEW_APP'},\n new Separator(' ════ Existing applications: ════ '),\n ...choices,\n ],\n loop: false,\n message: 'Would you like to create a new application deployment, or deploy to an existing one?',\n pageSize: 10,\n })\n\n if (selected === 'NEW_APP') {\n return null\n }\n\n return existing.find((app) => app.appHost === selected)!\n}\n\nasync function promptForExistingStudio({\n existing,\n urlType,\n}: {\n existing: UserApplication[]\n urlType: 'external' | 'internal'\n}): Promise<UserApplication | null> {\n const newLabel =\n urlType === 'external' ? 'Register new external studio URL' : 'Create new studio hostname'\n const selectMessage =\n urlType === 'external'\n ? 'Select existing external studio, or register a new one'\n : 'Select existing studio hostname, or create a new one'\n\n const choices = existing.map((app) => ({name: app.title ?? app.appHost, value: app.appHost}))\n\n const selected = await select({\n choices: [{name: newLabel, value: 'NEW_STUDIO'}, new Separator(), ...choices],\n message: selectMessage,\n })\n\n if (selected === 'NEW_STUDIO') {\n return null\n }\n\n return existing.find((app) => app.appHost === selected)!\n}\n"],"names":["select","Separator","spinner","createUserApplication","getAppId","getErrorMessage","createFailFastReporter","describeAppTarget","describeAppTargetError","describeStudioTarget","deployDebug","resolveAppDeployTarget","resolveStudioDeployTarget","findUserApplication","options","cliConfig","organizationId","output","title","unattended","spin","start","resolution","appId","error","clear","exit","type","succeed","application","info","promptForExistingApp","existing","message","report","findUserApplicationForStudio","isExternal","projectId","studioHost","urlFlag","urlType","fail","createFromConfiguredHost","appHost","length","promptForExistingStudio","log","response","appType","body","e","includes","statusCode","Error","choices","map","app","name","value","selected","loop","pageSize","find","newLabel","selectMessage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/findUserApplication.ts"],"sourcesContent":["/**\n * Finds (or creates) the user application a deploy targets — the interactive\n * adapter that turns the resolveDeployTarget verdicts into prompts, creation,\n * and exits. Dry runs consume the same verdicts read-only (see deployChecks).\n */\n\nimport {type CliConfig, type Output} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {select, Separator, spinner} from '@sanity/cli-core/ux'\n\nimport {\n createUserApplication,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {createFailFastReporter} from '../../util/checks.js'\nimport {describeAppTarget, describeAppTargetError, describeStudioTarget} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {resolveAppDeployTarget, resolveStudioDeployTarget} from './resolveDeployTarget.js'\n\ninterface FindUserApplicationOptions {\n cliConfig: CliConfig\n organizationId: string\n output: Output\n\n title?: string\n unattended?: boolean\n}\n\nexport async function findUserApplication(\n options: FindUserApplicationOptions,\n): Promise<UserApplicationResolved | null> {\n const {cliConfig, organizationId, output, title, unattended = false} = options\n const spin = spinner('Checking application info...').start()\n\n let resolution\n try {\n resolution = await resolveAppDeployTarget({appId: getAppId(cliConfig), organizationId})\n deployDebug('Resolved app deploy target', resolution)\n } catch (error) {\n spin.clear()\n deployDebug('Error finding user application for app', error)\n output.error(describeAppTargetError(error, organizationId), {exit: 1})\n return null\n }\n\n if (resolution.type === 'found') {\n spin.succeed()\n return resolution.application\n }\n\n // null tells the caller to create. Unattended runs can only create with a\n // --title; picking among existing apps (needs-input) always needs a prompt.\n if (resolution.type === 'would-create' && (!unattended || title)) {\n spin.info('No application ID configured')\n return null\n }\n if (resolution.type === 'needs-input' && !unattended) {\n spin.info('No application ID configured')\n return promptForExistingApp(resolution.existing)\n }\n\n spin.clear()\n // 'blocked' diagnoses as a skip (its root cause fails an earlier check), so it\n // needs an explicit exit here to not fall through to application creation\n if (resolution.type === 'blocked') {\n output.error(resolution.message, {exit: 1})\n return null\n }\n createFailFastReporter(output).report(describeAppTarget(resolution))\n return null\n}\n\ninterface FindUserApplicationForStudioOptions {\n isExternal: boolean\n output: Output\n projectId: string\n\n appId?: string\n studioHost?: string\n title?: string\n unattended?: boolean\n urlFlag?: string\n}\n\nexport async function findUserApplicationForStudio(\n options: FindUserApplicationForStudioOptions,\n): Promise<{application: UserApplication | null; created: boolean}> {\n const {\n appId,\n isExternal,\n output,\n projectId,\n studioHost,\n title,\n unattended = false,\n urlFlag,\n } = options\n const urlType = isExternal ? 'external' : 'internal'\n const spin = spinner('Checking project info').start()\n\n let resolution\n try {\n resolution = await resolveStudioDeployTarget({\n appId,\n isExternal,\n projectId,\n studioHost,\n urlFlag,\n })\n deployDebug('Resolved studio deploy target', resolution)\n } catch (error) {\n spin.fail()\n deployDebug('Error finding user application', error)\n output.error(`Failed to resolve deploy target: ${getErrorMessage(error)}`, {exit: 1})\n return {application: null, created: false}\n }\n\n if (resolution.type === 'found') {\n spin.succeed()\n return {application: resolution.application, created: false}\n }\n\n // The configured host isn't registered yet — a deploy registers it without\n // prompting, so this returns a newly created studio.\n if (resolution.type === 'would-create') {\n spin.succeed()\n const application = await createFromConfiguredHost({\n appHost: resolution.appHost,\n output,\n projectId,\n title,\n urlType,\n })\n return {application, created: application !== null}\n }\n\n if (resolution.type === 'needs-input' && !unattended) {\n spin.succeed()\n // Nothing to select from — the caller prompts for a brand new host\n if (resolution.existing.length === 0) return {application: null, created: false}\n // A selected existing studio is an update; \"new\" returns null and the caller registers it.\n return {\n application: await promptForExistingStudio({existing: resolution.existing, urlType}),\n created: false,\n }\n }\n\n spin.fail()\n // 'blocked' diagnoses as a skip (its root cause fails an earlier check), so it\n // needs an explicit exit here\n if (resolution.type === 'blocked') {\n output.error(resolution.message, {exit: 1})\n return {application: null, created: false}\n }\n createFailFastReporter(output).report(describeStudioTarget(resolution, {isExternal}))\n return {application: null, created: false}\n}\n\n/**\n * The host is configured (studioHost or --url) but not registered yet:\n * a deploy registers it without prompting.\n */\nasync function createFromConfiguredHost({\n appHost,\n output,\n projectId,\n title,\n urlType,\n}: {\n appHost: string\n output: Output\n projectId: string\n title?: string\n urlType: 'external' | 'internal'\n}): Promise<UserApplication | null> {\n if (urlType === 'external') {\n output.log('Your project has not been registered with an external studio URL.')\n output.log(`Registering ${appHost}`)\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log(`Creating https://${appHost}.sanity.studio`)\n }\n output.log('')\n\n const spin = spinner(\n urlType === 'external' ? 'Registering external studio' : 'Creating studio hostname',\n ).start()\n\n try {\n const response = await createUserApplication({\n appType: 'studio',\n body: {appHost, title, type: 'studio', urlType},\n projectId,\n })\n spin.succeed()\n return response\n } catch (e) {\n spin.fail()\n // if the name is taken, it should return a 409 so we relay to the user\n if ([402, 409].includes(e?.statusCode)) {\n output.error(e?.response?.body?.message || 'Bad request', {exit: 1})\n return null\n }\n // otherwise, it's a fatal error\n deployDebug('Error creating user application from config', e)\n output.error(\n `Error creating user application from config: ${e instanceof Error ? e.message : e}`,\n {exit: 1},\n )\n return null\n }\n}\n\nasync function promptForExistingApp(\n existing: UserApplicationResolved[],\n): Promise<UserApplicationResolved | null> {\n const choices = existing.map((app) => ({name: app.title ?? app.appHost, value: app.appHost}))\n\n const selected = await select({\n choices: [\n {name: 'New application deployment', value: 'NEW_APP'},\n new Separator(' ════ Existing applications: ════ '),\n ...choices,\n ],\n loop: false,\n message: 'Would you like to create a new application deployment, or deploy to an existing one?',\n pageSize: 10,\n })\n\n if (selected === 'NEW_APP') {\n return null\n }\n\n return existing.find((app) => app.appHost === selected)!\n}\n\nasync function promptForExistingStudio({\n existing,\n urlType,\n}: {\n existing: UserApplication[]\n urlType: 'external' | 'internal'\n}): Promise<UserApplication | null> {\n const newLabel =\n urlType === 'external' ? 'Register new external studio URL' : 'Create new studio hostname'\n const selectMessage =\n urlType === 'external'\n ? 'Select existing external studio, or register a new one'\n : 'Select existing studio hostname, or create a new one'\n\n const choices = existing.map((app) => ({name: app.title ?? app.appHost, value: app.appHost}))\n\n const selected = await select({\n choices: [{name: newLabel, value: 'NEW_STUDIO'}, new Separator(), ...choices],\n message: selectMessage,\n })\n\n if (selected === 'NEW_STUDIO') {\n return null\n }\n\n return existing.find((app) => app.appHost === selected)!\n}\n"],"names":["getErrorMessage","select","Separator","spinner","createUserApplication","getAppId","createFailFastReporter","describeAppTarget","describeAppTargetError","describeStudioTarget","deployDebug","resolveAppDeployTarget","resolveStudioDeployTarget","findUserApplication","options","cliConfig","organizationId","output","title","unattended","spin","start","resolution","appId","error","clear","exit","type","succeed","application","info","promptForExistingApp","existing","message","report","findUserApplicationForStudio","isExternal","projectId","studioHost","urlFlag","urlType","fail","created","createFromConfiguredHost","appHost","length","promptForExistingStudio","log","response","appType","body","e","includes","statusCode","Error","choices","map","app","name","value","selected","loop","pageSize","find","newLabel","selectMessage"],"mappings":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { getApplication, getApplicationUrl } from '@sanity/workbench-cli/deploy';
|
|
1
2
|
import { getUserApplication, getUserApplications } from '../../services/userApplications.js';
|
|
3
|
+
import { APP_ID_NOT_FOUND_IN_ORGANIZATION } from '../../util/errorMessages.js';
|
|
2
4
|
import { normalizeUrl, validateUrl } from './urlUtils.js';
|
|
3
5
|
/**
|
|
4
6
|
* Owns the studio deploy-target rules: the --url flag over studioHost config,
|
|
@@ -136,6 +138,48 @@ import { normalizeUrl, validateUrl } from './urlUtils.js';
|
|
|
136
138
|
type: 'would-create'
|
|
137
139
|
};
|
|
138
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* The dry-run counterpart to a workbench app's create-on-deploy: a configured
|
|
143
|
+
* `appId` is looked up read-only, otherwise a coreApp would be created.
|
|
144
|
+
* @internal
|
|
145
|
+
*/ export async function resolveWorkbenchApp({ appId }) {
|
|
146
|
+
return appId ? resolveAppById(appId) : {
|
|
147
|
+
type: 'would-create'
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The studio counterpart to {@link resolveWorkbenchApp}: a configured
|
|
152
|
+
* `studioHost` would create that hostname, and without one a deploy would prompt.
|
|
153
|
+
*
|
|
154
|
+
* @internal
|
|
155
|
+
*/ export async function resolveWorkbenchStudio({ appId, studioHost }) {
|
|
156
|
+
if (appId) return resolveAppById(appId);
|
|
157
|
+
if (studioHost) return {
|
|
158
|
+
appHost: studioHost,
|
|
159
|
+
type: 'would-create'
|
|
160
|
+
};
|
|
161
|
+
return {
|
|
162
|
+
existing: [],
|
|
163
|
+
type: 'needs-input'
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
async function resolveAppById(appId) {
|
|
167
|
+
const application = await getApplication(appId);
|
|
168
|
+
return application ? {
|
|
169
|
+
application: {
|
|
170
|
+
appHost: application.slug ?? '',
|
|
171
|
+
id: application.id,
|
|
172
|
+
organizationId: application.organizationId,
|
|
173
|
+
title: application.title,
|
|
174
|
+
url: getApplicationUrl(application)
|
|
175
|
+
},
|
|
176
|
+
type: 'found'
|
|
177
|
+
} : {
|
|
178
|
+
message: APP_ID_NOT_FOUND_IN_ORGANIZATION,
|
|
179
|
+
reason: 'app-not-found',
|
|
180
|
+
type: 'invalid'
|
|
181
|
+
};
|
|
182
|
+
}
|
|
139
183
|
async function listStudioApplications(projectId, isExternal) {
|
|
140
184
|
const urlType = isExternal ? 'external' : 'internal';
|
|
141
185
|
const applications = await getUserApplications({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/resolveDeployTarget.ts"],"sourcesContent":["import {\n getUserApplication,\n getUserApplications,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\n/**\n * The read-only outcome of resolving where a deploy would go.\n *\n * - `found` — the deploy targets this existing user 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 */\ntype CommonDeployTargetResolution<App extends UserApplication = UserApplication> =\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 =\n | CommonDeployTargetResolution\n | {appHost: string; type: 'would-create'}\n\nexport type AppDeployTargetResolution =\n | CommonDeployTargetResolution<UserApplicationResolved>\n | {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> {\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> {\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\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":["getUserApplication","getUserApplications","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","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} 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. */\ninterface 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; type: 'would-create'}\n\nexport type AppDeployTargetResolution<App = DeployTargetCoreApp> =\n | CommonDeployTargetResolution<App>\n | {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: a configured\n * `appId` is looked up read-only, otherwise a coreApp would be created.\n * @internal\n */\nexport async function resolveWorkbenchApp({\n appId,\n}: {\n appId: string | undefined\n}): Promise<AppDeployTargetResolution> {\n return appId ? resolveAppById(appId) : {type: 'would-create'}\n}\n\n/**\n * The studio counterpart to {@link resolveWorkbenchApp}: a configured\n * `studioHost` would create that hostname, and without one a deploy would prompt.\n *\n * @internal\n */\nexport async function resolveWorkbenchStudio({\n appId,\n studioHost,\n}: {\n appId: string | undefined\n studioHost: string | undefined\n}): Promise<StudioDeployTargetResolution> {\n if (appId) return resolveAppById(appId)\n if (studioHost) return {appHost: studioHost, type: 'would-create'}\n return {existing: [], type: 'needs-input'}\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","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","resolveAppById","resolveWorkbenchStudio","slug","id","title","urlType","applications","filter","normalized","hostname","replace","includes","test"],"mappings":"AAAA,SAAQA,cAAc,EAAEC,iBAAiB,QAAO,+BAA8B;AAE9E,SACEC,kBAAkB,EAClBC,mBAAmB,QAGd,qCAAoC;AAC3C,SAAQC,gCAAgC,QAAO,8BAA6B;AAC5E,SAAQC,YAAY,EAAEC,WAAW,QAAO,gBAAe;AA8CvD;;;;;;CAMC,GACD,OAAO,eAAeC,0BAA0BC,OAM/C;IACC,MAAM,EAACC,KAAK,EAAEC,UAAU,EAAEC,SAAS,EAAEC,UAAU,EAAEC,OAAO,EAAC,GAAGL;IAE5D,yEAAyE;IACzE,6EAA6E;IAC7E,uCAAuC;IACvC,IAAIC,OAAO;QACT,IAAI,CAACE,WAAW;YACd,OAAO;gBAACG,SAAS;gBAA4BC,MAAM;YAAS;QAC9D;QACA,MAAMC,cAAc,MAAMd,mBAAmB;YAACO;YAAOQ,UAAU;YAAON;QAAS;QAC/E,IAAIK,aAAa;YACf,OAAO;gBAACA;gBAAaD,MAAM;YAAO;QACpC;QACA,OAAO;YACLD,SAAS,CAAC,4BAA4B,EAAEL,OAAO;YAC/CS,QAAQ;YACRH,MAAM;QACR;IACF;IAEA,MAAM,EAACI,OAAOC,SAAS,EAAEC,MAAMC,YAAY,EAAC,GAAGC,eAAe;QAC5Db;QACAE;QACAY,KAAKX;IACP;IACA,IAAIO,WAAW;QACb,OAAO;YAACN,SAASM;YAAWF,QAAQ;YAAgBH,MAAM;QAAS;IACrE;IAEA,oEAAoE;IACpE,IAAIU,UAAUH;IACd,IAAIG,WAAWf,YAAY;QACzBe,UAAUpB,aAAaoB;QACvB,MAAMC,aAAapB,YAAYmB;QAC/B,IAAIC,eAAe,MAAM;YACvB,OAAO;gBAACZ,SAASY;gBAAYR,QAAQ;gBAAgBH,MAAM;YAAS;QACtE;IACF;IAEA,IAAIU,SAAS;QACX,IAAI,CAACd,WAAW;YACd,OAAO;gBAACG,SAAS;gBAA4BC,MAAM;YAAS;QAC9D;QACA,MAAMC,cAAc,MAAMd,mBAAmB;YAACuB;YAASR,UAAU;YAAON;QAAS;QACjF,IAAIK,aAAa;YACf,OAAO;gBAACA;gBAAaD,MAAM;YAAO;QACpC;QACA,OAAO;YAACU;YAASV,MAAM;QAAc;IACvC;IAEA,6DAA6D;IAC7D,2EAA2E;IAC3E,MAAMY,WAAWhB,YAAY,MAAMiB,uBAAuBjB,WAAWD,cAAc,EAAE;IACrF,OAAO;QAACiB;QAAUZ,MAAM;IAAa;AACvC;AAEA;;;;;;CAMC,GACD,OAAO,eAAec,uBAAuBrB,OAG5C;IACC,MAAM,EAACC,KAAK,EAAEqB,cAAc,EAAC,GAAGtB;IAEhC,IAAIC,OAAO;QACT,MAAMO,cAAc,MAAMd,mBAAmB;YAACO;YAAOQ,UAAU;QAAI;QACnE,IAAID,aAAa;YACf,OAAO;gBAACA;gBAAaD,MAAM;YAAO;QACpC;QACA,OAAO;YACLD,SAAS,CAAC,4BAA4B,EAAEL,OAAO;YAC/CS,QAAQ;YACRH,MAAM;QACR;IACF;IAEA,IAAI,CAACe,gBAAgB;QACnB,OAAO;YAAChB,SAAS;YAAiCC,MAAM;QAAS;IACnE;IAEA,MAAMY,WAAW,MAAMxB,oBAAoB;QAAC4B,SAAS;QAAWD;IAAc;IAC9E,IAAIH,UAAUK,QAAQ;QACpB,OAAO;YAACL;YAAUZ,MAAM;QAAa;IACvC;IAEA,OAAO;QAACA,MAAM;IAAc;AAC9B;AAEA;;;;CAIC,GACD,OAAO,eAAekB,oBAAoB,EACxCxB,KAAK,EAGN;IACC,OAAOA,QAAQyB,eAAezB,SAAS;QAACM,MAAM;IAAc;AAC9D;AAEA;;;;;CAKC,GACD,OAAO,eAAeoB,uBAAuB,EAC3C1B,KAAK,EACLG,UAAU,EAIX;IACC,IAAIH,OAAO,OAAOyB,eAAezB;IACjC,IAAIG,YAAY,OAAO;QAACa,SAASb;QAAYG,MAAM;IAAc;IACjE,OAAO;QAACY,UAAU,EAAE;QAAEZ,MAAM;IAAa;AAC3C;AAEA,eAAemB,eACbzB,KAAa;IAEb,MAAMO,cAAc,MAAMhB,eAAeS;IACzC,OAAOO,cACH;QACEA,aAAa;YACXS,SAAST,YAAYoB,IAAI,IAAI;YAC7BC,IAAIrB,YAAYqB,EAAE;YAClBP,gBAAgBd,YAAYc,cAAc;YAC1CQ,OAAOtB,YAAYsB,KAAK;YACxBd,KAAKvB,kBAAkBe;QACzB;QACAD,MAAM;IACR,IACA;QAACD,SAASV;QAAkCc,QAAQ;QAAiBH,MAAM;IAAS;AAC1F;AAEA,eAAea,uBACbjB,SAAiB,EACjBD,UAAmB;IAEnB,MAAM6B,UAAU7B,aAAa,aAAa;IAC1C,MAAM8B,eAAe,MAAMrC,oBAAoB;QAAC4B,SAAS;QAAUpB;IAAS;IAC5E,mEAAmE;IACnE,OAAO6B,cAAcC,OAAO,CAACzB,cAAgBA,YAAYuB,OAAO,KAAKA,YAAY,EAAE;AACrF;AAEA,SAAShB,eAAe,EACtBb,UAAU,EACVE,UAAU,EACVY,GAAG,EAKJ;IACC,IAAI,CAACA,KAAK;QACR,OAAO;YAACH,MAAMT;QAAU;IAC1B;IAEA,IAAIF,YAAY;QACd,MAAMgC,aAAarC,aAAamB;QAChC,MAAME,aAAapB,YAAYoC;QAC/B,IAAIhB,eAAe,MAAM;YACvB,OAAO;gBAACP,OAAOO;YAAU;QAC3B;QACA,OAAO;YAACL,MAAMqB;QAAU;IAC1B;IAEA,mFAAmF;IACnF,MAAMC,WAAWnB,IAAIoB,OAAO,CAAC,iBAAiB,IAAIA,OAAO,CAAC,yBAAyB;IAEnF,yFAAyF;IACzF,IAAID,SAASE,QAAQ,CAAC,MAAM;QAC1B,OAAO;YACL1B,OAAO,CAAC,CAAC,EAAEwB,SAAS,8EAA8E,CAAC;QACrG;IACF;IAEA,+DAA+D;IAC/D,IAAI,CAAC,mCAAmCG,IAAI,CAACH,WAAW;QACtD,OAAO;YACLxB,OAAO,CAAC,yBAAyB,EAAEwB,SAAS,4DAA4D,CAAC;QAC3G;IACF;IAEA,OAAO;QAACtB,MAAMsB;IAAQ;AACxB"}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { getSanityUrl } from '@sanity/cli-core';
|
|
2
|
-
export function getCoreAppUrl(organizationId, appId) {
|
|
3
|
-
return getSanityUrl(`/@${organizationId}/application/${appId}`);
|
|
4
|
-
}
|
|
5
1
|
/**
|
|
6
2
|
* Validates that the given string is a valid http or https URL.
|
|
7
3
|
* Returns `true` if valid, or an error message string if invalid.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/urlUtils.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/urlUtils.ts"],"sourcesContent":["/**\n * Validates that the given string is a valid http or https URL.\n * Returns `true` if valid, or an error message string if invalid.\n */\nexport function validateUrl(url: string): string | true {\n try {\n const parsed = new URL(url)\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return 'URL must use http or https protocol'\n }\n return true\n } catch {\n return 'Invalid URL. Please enter a valid http or https URL'\n }\n}\n\n/**\n * Normalizes a URL by removing trailing slashes.\n */\nexport function normalizeUrl(url: string): string {\n return url.replace(/\\/+$/, '')\n}\n"],"names":["validateUrl","url","parsed","URL","protocol","normalizeUrl","replace"],"mappings":"AAAA;;;CAGC,GACD,OAAO,SAASA,YAAYC,GAAW;IACrC,IAAI;QACF,MAAMC,SAAS,IAAIC,IAAIF;QACvB,IAAIC,OAAOE,QAAQ,KAAK,WAAWF,OAAOE,QAAQ,KAAK,UAAU;YAC/D,OAAO;QACT;QACA,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA;;CAEC,GACD,OAAO,SAASC,aAAaJ,GAAW;IACtC,OAAOA,IAAIK,OAAO,CAAC,QAAQ;AAC7B"}
|
|
@@ -38,7 +38,7 @@ export function getDevServerConfig({ cliConfig, flags, httpPort, output, workDir
|
|
|
38
38
|
// view: the runtime/federation skip the `./App` render path entirely.
|
|
39
39
|
entry: app?.entry,
|
|
40
40
|
exposes: isWorkbenchApp(app) ? {
|
|
41
|
-
|
|
41
|
+
config: app.config,
|
|
42
42
|
services: app.services,
|
|
43
43
|
views: app.views
|
|
44
44
|
} : undefined,
|
|
@@ -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} 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 ? {
|
|
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.name : 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","name"],"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { styleText } from 'node:util';
|
|
2
|
-
import { checkRequiredDependencies, checkStudioDependencyVersions } from '@sanity/cli-build/_internal/build';
|
|
2
|
+
import { checkRequiredDependencies, checkStudioDependencyVersions, compareDependencyVersions } from '@sanity/cli-build/_internal/build';
|
|
3
3
|
import { getLocalPackageVersion, isInteractive } from '@sanity/cli-core';
|
|
4
4
|
import { confirm, logSymbols, spinner } from '@sanity/cli-core/ux';
|
|
5
5
|
import { isWorkbenchApp } from '@sanity/workbench-cli';
|
|
@@ -8,7 +8,6 @@ import { startDevServer } from '../../../server/devServer.js';
|
|
|
8
8
|
import { gracefulServerDeath } from '../../../server/gracefulServerDeath.js';
|
|
9
9
|
import { getProjectById } from '../../../services/projects.js';
|
|
10
10
|
import { getAppId } from '../../../util/appId.js';
|
|
11
|
-
import { compareDependencyVersions } from '../../../util/compareDependencyVersions.js';
|
|
12
11
|
import { getPackageManagerChoice } from '../../../util/packageManager/packageManagerChoice.js';
|
|
13
12
|
import { upgradePackages } from '../../../util/packageManager/upgradePackages.js';
|
|
14
13
|
import { shouldAutoUpdate } from '../../build/shouldAutoUpdate.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/dev/servers/startStudioDevServer.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {\n checkRequiredDependencies,\n checkStudioDependencyVersions,\n} from '@sanity/cli-build/_internal/build'\nimport {getLocalPackageVersion, isInteractive} from '@sanity/cli-core'\nimport {confirm, logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {isWorkbenchApp} from '@sanity/workbench-cli'\nimport {parse as semverParse} from 'semver'\n\nimport {startDevServer} from '../../../server/devServer.js'\nimport {gracefulServerDeath} from '../../../server/gracefulServerDeath.js'\nimport {getProjectById} from '../../../services/projects.js'\nimport {getAppId} from '../../../util/appId.js'\nimport {compareDependencyVersions} from '../../../util/compareDependencyVersions.js'\nimport {getPackageManagerChoice} from '../../../util/packageManager/packageManagerChoice.js'\nimport {upgradePackages} from '../../../util/packageManager/upgradePackages.js'\nimport {shouldAutoUpdate} from '../../build/shouldAutoUpdate.js'\nimport {devDebug} from '../devDebug.js'\nimport {type DevActionOptions, type StartDevServerResult} from '../types.js'\nimport {getDashboardAppURL} from './getDashboardAppUrl.js'\nimport {getDevServerConfig} from './getDevServerConfig.js'\n\nexport async function startStudioDevServer(\n options: DevActionOptions,\n): Promise<StartDevServerResult> {\n const {announceUrl = true, cliConfig, flags, httpPort, output, workDir} = options\n const projectId = cliConfig?.api?.projectId\n let organizationId: string | undefined\n\n // Workbench apps don't load through the dashboard, so the flag has no\n // meaning for them and is ignored.\n const loadInDashboard = flags['load-in-dashboard'] && !isWorkbenchApp(cliConfig?.app)\n\n // Check studio dependency versions\n await checkStudioDependencyVersions(workDir, output)\n\n const {installedSanityVersion} = await checkRequiredDependencies(options)\n\n // Check if auto-updates are enabled\n const autoUpdatesEnabled = shouldAutoUpdate({cliConfig, flags, output})\n\n if (autoUpdatesEnabled) {\n // Get the clean version without build metadata: https://semver.org/#spec-item-10\n const cleanSanityVersion = semverParse(installedSanityVersion)?.version\n if (!cleanSanityVersion) {\n throw new Error(`Failed to parse installed Sanity version: ${installedSanityVersion}`)\n }\n\n const sanityDependencies = [\n {name: 'sanity', version: cleanSanityVersion},\n {name: '@sanity/vision', version: cleanSanityVersion},\n ]\n\n output.log(`${logSymbols.info} Running with auto-updates enabled`)\n\n // Check local versions against deployed versions\n let result: Awaited<ReturnType<typeof compareDependencyVersions>> | undefined\n\n const appId = getAppId(cliConfig)\n\n try {\n result = await compareDependencyVersions(sanityDependencies, workDir, {appId})\n } catch (err) {\n output.warn(`Failed to compare local versions against auto-updating versions: ${err}`)\n }\n\n if (result?.unresolvedPrerelease.length) {\n for (const mod of result.unresolvedPrerelease) {\n output.warn(\n `Your local version of ${mod.pkg} (${mod.version}) is a prerelease not available on the auto-updates CDN. The locally installed version will be used.`,\n )\n }\n }\n\n // mismatch between local and auto-updating dependencies\n if (result?.mismatched.length) {\n const message =\n `The following local package versions are different from the versions currently served at runtime.\\n` +\n `When using auto updates, we recommend that you run with the same versions locally as will be used when deploying.\\n\\n` +\n `${result.mismatched.map((mod) => ` - ${mod.pkg} (local version: ${mod.installed}, runtime version: ${mod.remote})`).join('\\n')}\\n\\n`\n\n if (isInteractive()) {\n const shouldUpgrade = await confirm({\n default: true,\n message: styleText('yellow', `${message}Do you want to upgrade local versions?`),\n })\n if (shouldUpgrade) {\n await upgradePackages(\n {\n packageManager: (await getPackageManagerChoice(workDir, {interactive: false})).chosen,\n packages: result.mismatched.map((res) => [res.pkg, res.remote]),\n },\n {output, workDir},\n )\n }\n } else {\n // In this case we warn the user but we don't ask them if they want to upgrade because it's not interactive.\n output.log(styleText('yellow', message))\n }\n }\n }\n\n if (cliConfig?.schemaExtraction?.enabled) {\n output.log(`${logSymbols.info} Running dev server with schema extraction enabled`)\n }\n\n const config = getDevServerConfig({cliConfig, flags, httpPort, output, workDir})\n\n if (loadInDashboard) {\n if (!projectId) {\n output.error('Project Id is required to load in dashboard', {exit: 1})\n }\n\n try {\n const project = await getProjectById(projectId!)\n organizationId = project.organizationId!\n } catch (error) {\n devDebug('Error getting organization id from project id', error)\n output.error('Failed to get organization id from project id', {exit: 1})\n }\n }\n\n try {\n const startTime = Date.now()\n const spin = spinner('Starting dev server').start()\n const {close, server} = await startDevServer(config)\n\n const {info: loggerInfo} = server.config.logger\n const {port} = server.config.server\n const httpHost = config.httpHost || 'localhost'\n\n if (loadInDashboard) {\n spin.succeed()\n\n output.log(`Dev server started on port ${port}`)\n output.log(`View your studio in the Sanity dashboard here:`)\n output.log(\n styleText(\n ['blue', 'underline'],\n await getDashboardAppURL({\n httpHost,\n httpPort: port,\n organizationId: organizationId!,\n }),\n ),\n )\n } else {\n const startupDuration = Date.now() - startTime\n const url = `http://${httpHost || 'localhost'}:${port}${config.basePath}`\n const appType = 'Sanity Studio'\n\n const viteVersion = await getLocalPackageVersion('vite', import.meta.url)\n spin.succeed()\n\n loggerInfo(\n `${appType} ` +\n `using ${styleText('cyan', `vite@${viteVersion}`)} ` +\n `ready in ${styleText('cyan', `${Math.ceil(startupDuration)}ms`)}` +\n (announceUrl ? ` and running at ${styleText('cyan', url)}` : ''),\n )\n }\n\n return {close, server, started: true}\n } catch (err) {\n devDebug('Error starting studio dev server', err)\n throw gracefulServerDeath('dev', config.httpHost, config.httpPort, err)\n }\n}\n"],"names":["styleText","checkRequiredDependencies","checkStudioDependencyVersions","getLocalPackageVersion","isInteractive","confirm","logSymbols","spinner","isWorkbenchApp","parse","semverParse","startDevServer","gracefulServerDeath","getProjectById","getAppId","compareDependencyVersions","getPackageManagerChoice","upgradePackages","shouldAutoUpdate","devDebug","getDashboardAppURL","getDevServerConfig","startStudioDevServer","options","announceUrl","cliConfig","flags","httpPort","output","workDir","projectId","api","organizationId","loadInDashboard","app","installedSanityVersion","autoUpdatesEnabled","cleanSanityVersion","version","Error","sanityDependencies","name","log","info","result","appId","err","warn","unresolvedPrerelease","length","mod","pkg","mismatched","message","map","installed","remote","join","shouldUpgrade","default","packageManager","interactive","chosen","packages","res","schemaExtraction","enabled","config","error","exit","project","startTime","Date","now","spin","start","close","server","loggerInfo","logger","port","httpHost","succeed","startupDuration","url","basePath","appType","viteVersion","Math","ceil","started"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/dev/servers/startStudioDevServer.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {\n checkRequiredDependencies,\n checkStudioDependencyVersions,\n compareDependencyVersions,\n} from '@sanity/cli-build/_internal/build'\nimport {getLocalPackageVersion, isInteractive} from '@sanity/cli-core'\nimport {confirm, logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {isWorkbenchApp} from '@sanity/workbench-cli'\nimport {parse as semverParse} from 'semver'\n\nimport {startDevServer} from '../../../server/devServer.js'\nimport {gracefulServerDeath} from '../../../server/gracefulServerDeath.js'\nimport {getProjectById} from '../../../services/projects.js'\nimport {getAppId} from '../../../util/appId.js'\nimport {getPackageManagerChoice} from '../../../util/packageManager/packageManagerChoice.js'\nimport {upgradePackages} from '../../../util/packageManager/upgradePackages.js'\nimport {shouldAutoUpdate} from '../../build/shouldAutoUpdate.js'\nimport {devDebug} from '../devDebug.js'\nimport {type DevActionOptions, type StartDevServerResult} from '../types.js'\nimport {getDashboardAppURL} from './getDashboardAppUrl.js'\nimport {getDevServerConfig} from './getDevServerConfig.js'\n\nexport async function startStudioDevServer(\n options: DevActionOptions,\n): Promise<StartDevServerResult> {\n const {announceUrl = true, cliConfig, flags, httpPort, output, workDir} = options\n const projectId = cliConfig?.api?.projectId\n let organizationId: string | undefined\n\n // Workbench apps don't load through the dashboard, so the flag has no\n // meaning for them and is ignored.\n const loadInDashboard = flags['load-in-dashboard'] && !isWorkbenchApp(cliConfig?.app)\n\n // Check studio dependency versions\n await checkStudioDependencyVersions(workDir, output)\n\n const {installedSanityVersion} = await checkRequiredDependencies(options)\n\n // Check if auto-updates are enabled\n const autoUpdatesEnabled = shouldAutoUpdate({cliConfig, flags, output})\n\n if (autoUpdatesEnabled) {\n // Get the clean version without build metadata: https://semver.org/#spec-item-10\n const cleanSanityVersion = semverParse(installedSanityVersion)?.version\n if (!cleanSanityVersion) {\n throw new Error(`Failed to parse installed Sanity version: ${installedSanityVersion}`)\n }\n\n const sanityDependencies = [\n {name: 'sanity', version: cleanSanityVersion},\n {name: '@sanity/vision', version: cleanSanityVersion},\n ]\n\n output.log(`${logSymbols.info} Running with auto-updates enabled`)\n\n // Check local versions against deployed versions\n let result: Awaited<ReturnType<typeof compareDependencyVersions>> | undefined\n\n const appId = getAppId(cliConfig)\n\n try {\n result = await compareDependencyVersions(sanityDependencies, workDir, {appId})\n } catch (err) {\n output.warn(`Failed to compare local versions against auto-updating versions: ${err}`)\n }\n\n if (result?.unresolvedPrerelease.length) {\n for (const mod of result.unresolvedPrerelease) {\n output.warn(\n `Your local version of ${mod.pkg} (${mod.version}) is a prerelease not available on the auto-updates CDN. The locally installed version will be used.`,\n )\n }\n }\n\n // mismatch between local and auto-updating dependencies\n if (result?.mismatched.length) {\n const message =\n `The following local package versions are different from the versions currently served at runtime.\\n` +\n `When using auto updates, we recommend that you run with the same versions locally as will be used when deploying.\\n\\n` +\n `${result.mismatched.map((mod) => ` - ${mod.pkg} (local version: ${mod.installed}, runtime version: ${mod.remote})`).join('\\n')}\\n\\n`\n\n if (isInteractive()) {\n const shouldUpgrade = await confirm({\n default: true,\n message: styleText('yellow', `${message}Do you want to upgrade local versions?`),\n })\n if (shouldUpgrade) {\n await upgradePackages(\n {\n packageManager: (await getPackageManagerChoice(workDir, {interactive: false})).chosen,\n packages: result.mismatched.map((res) => [res.pkg, res.remote]),\n },\n {output, workDir},\n )\n }\n } else {\n // In this case we warn the user but we don't ask them if they want to upgrade because it's not interactive.\n output.log(styleText('yellow', message))\n }\n }\n }\n\n if (cliConfig?.schemaExtraction?.enabled) {\n output.log(`${logSymbols.info} Running dev server with schema extraction enabled`)\n }\n\n const config = getDevServerConfig({cliConfig, flags, httpPort, output, workDir})\n\n if (loadInDashboard) {\n if (!projectId) {\n output.error('Project Id is required to load in dashboard', {exit: 1})\n }\n\n try {\n const project = await getProjectById(projectId!)\n organizationId = project.organizationId!\n } catch (error) {\n devDebug('Error getting organization id from project id', error)\n output.error('Failed to get organization id from project id', {exit: 1})\n }\n }\n\n try {\n const startTime = Date.now()\n const spin = spinner('Starting dev server').start()\n const {close, server} = await startDevServer(config)\n\n const {info: loggerInfo} = server.config.logger\n const {port} = server.config.server\n const httpHost = config.httpHost || 'localhost'\n\n if (loadInDashboard) {\n spin.succeed()\n\n output.log(`Dev server started on port ${port}`)\n output.log(`View your studio in the Sanity dashboard here:`)\n output.log(\n styleText(\n ['blue', 'underline'],\n await getDashboardAppURL({\n httpHost,\n httpPort: port,\n organizationId: organizationId!,\n }),\n ),\n )\n } else {\n const startupDuration = Date.now() - startTime\n const url = `http://${httpHost || 'localhost'}:${port}${config.basePath}`\n const appType = 'Sanity Studio'\n\n const viteVersion = await getLocalPackageVersion('vite', import.meta.url)\n spin.succeed()\n\n loggerInfo(\n `${appType} ` +\n `using ${styleText('cyan', `vite@${viteVersion}`)} ` +\n `ready in ${styleText('cyan', `${Math.ceil(startupDuration)}ms`)}` +\n (announceUrl ? ` and running at ${styleText('cyan', url)}` : ''),\n )\n }\n\n return {close, server, started: true}\n } catch (err) {\n devDebug('Error starting studio dev server', err)\n throw gracefulServerDeath('dev', config.httpHost, config.httpPort, err)\n }\n}\n"],"names":["styleText","checkRequiredDependencies","checkStudioDependencyVersions","compareDependencyVersions","getLocalPackageVersion","isInteractive","confirm","logSymbols","spinner","isWorkbenchApp","parse","semverParse","startDevServer","gracefulServerDeath","getProjectById","getAppId","getPackageManagerChoice","upgradePackages","shouldAutoUpdate","devDebug","getDashboardAppURL","getDevServerConfig","startStudioDevServer","options","announceUrl","cliConfig","flags","httpPort","output","workDir","projectId","api","organizationId","loadInDashboard","app","installedSanityVersion","autoUpdatesEnabled","cleanSanityVersion","version","Error","sanityDependencies","name","log","info","result","appId","err","warn","unresolvedPrerelease","length","mod","pkg","mismatched","message","map","installed","remote","join","shouldUpgrade","default","packageManager","interactive","chosen","packages","res","schemaExtraction","enabled","config","error","exit","project","startTime","Date","now","spin","start","close","server","loggerInfo","logger","port","httpHost","succeed","startupDuration","url","basePath","appType","viteVersion","Math","ceil","started"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/documents/types.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core'\nimport {type ClientConfig} from '@sanity/client'\nimport {type ValidationMarker} from '@sanity/types'\nimport {type WorkerChannel, type WorkerChannelReceiver} from '@sanity/worker-channels'\n\nimport {type ValidateDocumentsCommand} from '../../commands/documents/validate.js'\n\nexport type Level = ValidationMarker['level']\n\n/** @internal */\nexport interface ValidateDocumentsWorkerData {\n workDir: string\n\n clientConfig?: Partial<ClientConfig>\n dataset?: string\n level?: ValidationMarker['level']\n maxCustomValidationConcurrency?: number\n maxFetchConcurrency?: number\n ndjsonFilePath?: string\n projectId?: string\n studioHost?: string\n workspace?: string\n}\n\n/** @internal */\nexport type ValidationWorkerChannel = WorkerChannel.Definition<{\n exportFinished: WorkerChannel.Event<{totalDocumentsToValidate: number}>\n exportProgress: WorkerChannel.Stream<{documentCount: number; downloadedCount: number}>\n loadedDocumentCount: WorkerChannel.Event<{documentCount: number}>\n loadedReferenceIntegrity: WorkerChannel.Event\n loadedWorkspace: WorkerChannel.Event<{\n basePath: string\n dataset: string\n name: string\n projectId: string\n }>\n validation: WorkerChannel.Stream<{\n documentId: string\n documentType: string\n intentUrl?: string\n level: ValidationMarker['level']\n markers: ValidationMarker[]\n revision: string\n validatedCount: number\n }>\n}>\n\n/**\n * Combines the package's receiver API with a `dispose()` method that\n * unsubscribes from worker messages AND terminates the worker thread.\n */\nexport interface ValidationReceiver {\n dispose: () => Promise<number>\n event: WorkerChannelReceiver<ValidationWorkerChannel>['event']\n stream: WorkerChannelReceiver<ValidationWorkerChannel>['stream']\n}\n\nexport type BuiltInValidationReporter = (options: {\n flags: ValidateDocumentsCommand['flags']\n output: Output\n worker: ValidationReceiver\n}) => Promise<Level>\n"],"names":[],"mappings":"AAyDA,WAIoB"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/documents/types.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core/types'\nimport {type ClientConfig} from '@sanity/client'\nimport {type ValidationMarker} from '@sanity/types'\nimport {type WorkerChannel, type WorkerChannelReceiver} from '@sanity/worker-channels'\n\nimport {type ValidateDocumentsCommand} from '../../commands/documents/validate.js'\n\nexport type Level = ValidationMarker['level']\n\n/** @internal */\nexport interface ValidateDocumentsWorkerData {\n workDir: string\n\n clientConfig?: Partial<ClientConfig>\n dataset?: string\n level?: ValidationMarker['level']\n maxCustomValidationConcurrency?: number\n maxFetchConcurrency?: number\n ndjsonFilePath?: string\n projectId?: string\n studioHost?: string\n workspace?: string\n}\n\n/** @internal */\nexport type ValidationWorkerChannel = WorkerChannel.Definition<{\n exportFinished: WorkerChannel.Event<{totalDocumentsToValidate: number}>\n exportProgress: WorkerChannel.Stream<{documentCount: number; downloadedCount: number}>\n loadedDocumentCount: WorkerChannel.Event<{documentCount: number}>\n loadedReferenceIntegrity: WorkerChannel.Event\n loadedWorkspace: WorkerChannel.Event<{\n basePath: string\n dataset: string\n name: string\n projectId: string\n }>\n validation: WorkerChannel.Stream<{\n documentId: string\n documentType: string\n intentUrl?: string\n level: ValidationMarker['level']\n markers: ValidationMarker[]\n revision: string\n validatedCount: number\n }>\n}>\n\n/**\n * Combines the package's receiver API with a `dispose()` method that\n * unsubscribes from worker messages AND terminates the worker thread.\n */\nexport interface ValidationReceiver {\n dispose: () => Promise<number>\n event: WorkerChannelReceiver<ValidationWorkerChannel>['event']\n stream: WorkerChannelReceiver<ValidationWorkerChannel>['stream']\n}\n\nexport type BuiltInValidationReporter = (options: {\n flags: ValidateDocumentsCommand['flags']\n output: Output\n worker: ValidationReceiver\n}) => Promise<Level>\n"],"names":[],"mappings":"AAyDA,WAIoB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getGlobalCliClient } from '@sanity/cli-core/apiClient';
|
|
2
|
+
import { createStudioWorker } from '@sanity/cli-core/tasks';
|
|
2
3
|
import { WorkerChannelReceiver } from '@sanity/worker-channels';
|
|
3
4
|
import { DOCUMENTS_API_VERSION } from './constants.js';
|
|
4
5
|
const defaultReporter = ({ dispose, stream })=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/documents/validate.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/documents/validate.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core/apiClient'\nimport {createStudioWorker} from '@sanity/cli-core/tasks'\nimport {type ClientConfig} from '@sanity/client'\nimport {type ValidationMarker} from '@sanity/types'\nimport {WorkerChannelReceiver} from '@sanity/worker-channels'\n\nimport {DOCUMENTS_API_VERSION} from './constants.js'\nimport {\n Level,\n type ValidateDocumentsWorkerData,\n type ValidationReceiver,\n type ValidationWorkerChannel,\n} from './types.js'\n\ninterface ValidateDocumentsOptions<TReturn = unknown> {\n dataset?: string // override\n level?: Level\n maxCustomValidationConcurrency?: number\n maxFetchConcurrency?: number\n ndjsonFilePath?: string\n projectId?: string // override\n reporter?: (worker: ValidationReceiver) => TReturn\n studioHost?: string\n workDir?: string\n workspace?: string\n}\n\ninterface DocumentValidationResult {\n documentId: string\n documentType: string\n level: ValidationMarker['level']\n markers: ValidationMarker[]\n revision: string\n}\n\nconst defaultReporter = ({dispose, stream}: ValidationReceiver) => {\n async function* createValidationGenerator() {\n for await (const {documentId, documentType, level, markers, revision} of stream.validation()) {\n const result: DocumentValidationResult = {\n documentId,\n documentType,\n level,\n markers,\n revision,\n }\n\n yield result\n }\n\n await dispose()\n }\n\n return createValidationGenerator()\n}\n\nexport function validateDocuments<TReturn>(\n options: Required<Pick<ValidateDocumentsOptions<TReturn>, 'reporter'>> &\n ValidateDocumentsOptions<TReturn>,\n): Promise<TReturn>\nexport function validateDocuments(\n options: ValidateDocumentsOptions,\n): Promise<AsyncIterable<DocumentValidationResult>>\nexport async function validateDocuments(options: ValidateDocumentsOptions): Promise<unknown> {\n const {\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n reporter = defaultReporter,\n workDir = process.cwd(),\n workspace,\n } = options\n\n const apiClient = await getGlobalCliClient({\n apiVersion: DOCUMENTS_API_VERSION,\n requireUser: true,\n })\n\n const clientConfig: ClientConfig = {\n ...apiClient.config(),\n // we set this explictly to true because we pass in a token via the\n // `clientConfiguration` object and also mock a browser environment in\n // this worker which triggers the browser warning\n ignoreBrowserTokenWarning: true,\n // Removing from object so config can be serialized\n // before sent to validation worker\n requester: undefined,\n // we set this explictly to true because the default client configuration\n // from the CLI comes configured with `useProjectHostname: false` when\n // `requireProject` is set to false\n useProjectHostname: true,\n }\n\n const worker = createStudioWorker(new URL('validateDocuments.worker.js', import.meta.url), {\n name: 'validateDocuments',\n studioRootPath: workDir,\n workerData: {\n // removes props in the config that make this object fail to serialize\n clientConfig: structuredClone(clientConfig),\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n studioHost: options.studioHost,\n workDir,\n workspace,\n } satisfies ValidateDocumentsWorkerData,\n })\n\n const receiver = WorkerChannelReceiver.from<ValidationWorkerChannel>(worker)\n const validationReceiver: ValidationReceiver = {\n dispose: async () => {\n receiver.unsubscribe()\n return worker.terminate()\n },\n event: receiver.event,\n stream: receiver.stream,\n }\n\n return reporter(validationReceiver)\n}\n"],"names":["getGlobalCliClient","createStudioWorker","WorkerChannelReceiver","DOCUMENTS_API_VERSION","defaultReporter","dispose","stream","createValidationGenerator","documentId","documentType","level","markers","revision","validation","result","validateDocuments","options","dataset","maxCustomValidationConcurrency","maxFetchConcurrency","ndjsonFilePath","projectId","reporter","workDir","process","cwd","workspace","apiClient","apiVersion","requireUser","clientConfig","config","ignoreBrowserTokenWarning","requester","undefined","useProjectHostname","worker","URL","url","name","studioRootPath","workerData","structuredClone","studioHost","receiver","from","validationReceiver","unsubscribe","terminate","event"],"mappings":"AAAA,SAAQA,kBAAkB,QAAO,6BAA4B;AAC7D,SAAQC,kBAAkB,QAAO,yBAAwB;AAGzD,SAAQC,qBAAqB,QAAO,0BAAyB;AAE7D,SAAQC,qBAAqB,QAAO,iBAAgB;AA6BpD,MAAMC,kBAAkB,CAAC,EAACC,OAAO,EAAEC,MAAM,EAAqB;IAC5D,gBAAgBC;QACd,WAAW,MAAM,EAACC,UAAU,EAAEC,YAAY,EAAEC,KAAK,EAAEC,OAAO,EAAEC,QAAQ,EAAC,IAAIN,OAAOO,UAAU,GAAI;YAC5F,MAAMC,SAAmC;gBACvCN;gBACAC;gBACAC;gBACAC;gBACAC;YACF;YAEA,MAAME;QACR;QAEA,MAAMT;IACR;IAEA,OAAOE;AACT;AASA,OAAO,eAAeQ,kBAAkBC,OAAiC;IACvE,MAAM,EACJC,OAAO,EACPP,KAAK,EACLQ,8BAA8B,EAC9BC,mBAAmB,EACnBC,cAAc,EACdC,SAAS,EACTC,WAAWlB,eAAe,EAC1BmB,UAAUC,QAAQC,GAAG,EAAE,EACvBC,SAAS,EACV,GAAGV;IAEJ,MAAMW,YAAY,MAAM3B,mBAAmB;QACzC4B,YAAYzB;QACZ0B,aAAa;IACf;IAEA,MAAMC,eAA6B;QACjC,GAAGH,UAAUI,MAAM,EAAE;QACrB,mEAAmE;QACnE,sEAAsE;QACtE,iDAAiD;QACjDC,2BAA2B;QAC3B,mDAAmD;QACnD,mCAAmC;QACnCC,WAAWC;QACX,yEAAyE;QACzE,sEAAsE;QACtE,mCAAmC;QACnCC,oBAAoB;IACtB;IAEA,MAAMC,SAASnC,mBAAmB,IAAIoC,IAAI,+BAA+B,YAAYC,GAAG,GAAG;QACzFC,MAAM;QACNC,gBAAgBjB;QAChBkB,YAAY;YACV,sEAAsE;YACtEX,cAAcY,gBAAgBZ;YAC9Bb;YACAP;YACAQ;YACAC;YACAC;YACAC;YACAsB,YAAY3B,QAAQ2B,UAAU;YAC9BpB;YACAG;QACF;IACF;IAEA,MAAMkB,WAAW1C,sBAAsB2C,IAAI,CAA0BT;IACrE,MAAMU,qBAAyC;QAC7CzC,SAAS;YACPuC,SAASG,WAAW;YACpB,OAAOX,OAAOY,SAAS;QACzB;QACAC,OAAOL,SAASK,KAAK;QACrB3C,QAAQsC,SAAStC,MAAM;IACzB;IAEA,OAAOgB,SAASwB;AAClB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/graphql/SchemaError.ts"],"sourcesContent":["import {formatSchemaValidation} from '@sanity/cli-build/_internal/extract'\nimport {type Output} from '@sanity/cli-core'\nimport {type SchemaValidationProblemGroup} from '@sanity/types'\n\nexport class SchemaError extends Error {\n problemGroups: SchemaValidationProblemGroup[]\n\n constructor(problemGroups: SchemaValidationProblemGroup[]) {\n super('Schema errors encountered')\n this.name = 'SchemaError'\n this.problemGroups = problemGroups\n }\n\n print(output: Output, label?: string): void {\n output.warn(`${label ?? 'Found errors in schema'}:\\n`)\n output.log(formatSchemaValidation(this.problemGroups))\n }\n}\n"],"names":["formatSchemaValidation","SchemaError","Error","problemGroups","name","print","output","label","warn","log"],"mappings":"AAAA,SAAQA,sBAAsB,QAAO,sCAAqC;AAI1E,OAAO,MAAMC,oBAAoBC;IAC/BC,cAA6C;IAE7C,YAAYA,aAA6C,CAAE;QACzD,KAAK,CAAC;QACN,IAAI,CAACC,IAAI,GAAG;QACZ,IAAI,CAACD,aAAa,GAAGA;IACvB;IAEAE,MAAMC,MAAc,EAAEC,KAAc,EAAQ;QAC1CD,OAAOE,IAAI,CAAC,GAAGD,SAAS,yBAAyB,GAAG,CAAC;QACrDD,OAAOG,GAAG,CAACT,uBAAuB,IAAI,CAACG,aAAa;IACtD;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/graphql/SchemaError.ts"],"sourcesContent":["import {formatSchemaValidation} from '@sanity/cli-build/_internal/extract'\nimport {type Output} from '@sanity/cli-core/types'\nimport {type SchemaValidationProblemGroup} from '@sanity/types'\n\nexport class SchemaError extends Error {\n problemGroups: SchemaValidationProblemGroup[]\n\n constructor(problemGroups: SchemaValidationProblemGroup[]) {\n super('Schema errors encountered')\n this.name = 'SchemaError'\n this.problemGroups = problemGroups\n }\n\n print(output: Output, label?: string): void {\n output.warn(`${label ?? 'Found errors in schema'}:\\n`)\n output.log(formatSchemaValidation(this.problemGroups))\n }\n}\n"],"names":["formatSchemaValidation","SchemaError","Error","problemGroups","name","print","output","label","warn","log"],"mappings":"AAAA,SAAQA,sBAAsB,QAAO,sCAAqC;AAI1E,OAAO,MAAMC,oBAAoBC;IAC/BC,cAA6C;IAE7C,YAAYA,aAA6C,CAAE;QACzD,KAAK,CAAC;QACN,IAAI,CAACC,IAAI,GAAG;QACZ,IAAI,CAACD,aAAa,GAAGA;IACvB;IAEAE,MAAMC,MAAc,EAAEC,KAAc,EAAQ;QAC1CD,OAAOE,IAAI,CAAC,GAAGD,SAAS,yBAAyB,GAAG,CAAC;QACrDD,OAAOG,GAAG,CAACT,uBAAuB,IAAI,CAACG,aAAa;IACtD;AACF"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { isMainThread } from 'node:worker_threads';
|
|
2
|
-
import { findStudioConfigPath, getCliConfig
|
|
2
|
+
import { findStudioConfigPath, getCliConfig } from '@sanity/cli-core/config';
|
|
3
|
+
import { subdebug } from '@sanity/cli-core/debug';
|
|
4
|
+
import { studioWorkerTask } from '@sanity/cli-core/tasks';
|
|
3
5
|
import { isSchemaError } from '../../util/isSchemaError.js';
|
|
4
6
|
import { extractFromSanitySchema } from './extractFromSanitySchema.js';
|
|
5
7
|
import { resolveGraphQLApis } from './resolveGraphQLApisFromWorkspaces.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/graphql/extractGraphQLAPIs.ts"],"sourcesContent":["import {isMainThread, type MessagePort} from 'node:worker_threads'\n\nimport {\n type CliConfig,\n findStudioConfigPath,\n getCliConfig,\n studioWorkerTask,\n subdebug,\n} from '@sanity/cli-core'\n\nimport {isSchemaError} from '../../util/isSchemaError.js'\nimport {extractFromSanitySchema} from './extractFromSanitySchema.js'\nimport {resolveGraphQLApis, type Workspace} from './resolveGraphQLApisFromWorkspaces.js'\nimport {SchemaError} from './SchemaError.js'\nimport {\n type ExtractedGraphQLAPI,\n type GraphQLAPIConfig,\n type GraphQLWorkerResult,\n type SchemaDefinitionish,\n} from './types.js'\n\n// ---------------------------------------------------------------------------\n// Main-thread orchestrator\n// ---------------------------------------------------------------------------\n\nexport async function extractGraphQLAPIs(\n workDir: string,\n options: {nonNullDocumentFieldsFlag?: boolean; withUnionCache?: boolean},\n): Promise<ExtractedGraphQLAPI[]> {\n if (!isMainThread) {\n throw new Error('extractGraphQLAPIs() must be called from the main thread')\n }\n\n const [cliConfig, configPath] = await Promise.all([\n getCliConfig(workDir),\n findStudioConfigPath(workDir),\n ])\n\n const result = await studioWorkerTask<GraphQLWorkerResult>(\n new URL('extractGraphQLAPIs.worker.js', import.meta.url),\n {\n name: 'extractGraphQLAPIs',\n studioRootPath: workDir,\n workerData: {\n cliConfig: extractGraphQLConfig(cliConfig),\n configPath,\n nonNullDocumentFieldsFlag: options.nonNullDocumentFieldsFlag,\n withUnionCache: options.withUnionCache,\n workDir,\n },\n },\n )\n\n if (result.configErrors?.length) {\n throw new SchemaError(result.configErrors)\n }\n\n return result.apis\n}\n\nfunction extractGraphQLConfig(config: CliConfig) {\n return {\n graphql: config.graphql,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Worker logic (runs inside the worker thread)\n// ---------------------------------------------------------------------------\n\nexport interface ExtractWorkerData {\n configPath: string\n workDir: string\n\n cliConfig?: {graphql?: GraphQLAPIConfig[]}\n nonNullDocumentFieldsFlag?: boolean\n withUnionCache?: boolean\n}\n\nexport interface ExtractWorkerDeps {\n getStudioWorkspaces: (configPath: string) => Promise<unknown[]>\n resolveLocalPackage: <T>(name: string, workDir: string) => Promise<T>\n}\n\nconst debug = subdebug('graphql:extractGraphQLAPIs:worker')\n\nexport async function extractGraphQLAPIsWorker(\n port: MessagePort,\n data: ExtractWorkerData,\n deps: ExtractWorkerDeps,\n): Promise<void> {\n const {cliConfig, configPath, nonNullDocumentFieldsFlag, withUnionCache, workDir} = data\n\n // Load workspaces — this loads sanity.config.ts through Vite, caching `sanity` in the process\n let workspaces: Workspace[]\n try {\n workspaces = (await deps.getStudioWorkspaces(configPath)) as Workspace[]\n } catch (err) {\n if (isSchemaError(err)) {\n const validation = err.schema._validation ?? []\n const configErrors = validation\n .map((g) => ({...g, problems: g.problems.filter((p) => p.severity === 'error')}))\n .filter((g) => g.problems.length > 0)\n\n // Only treat error-severity problems as schema errors. If the validation\n // only contains warnings, re-throw the original error so it isn't silently\n // swallowed — warnings alone should not block deployment.\n if (configErrors.length === 0) {\n throw err\n }\n\n port.postMessage({apis: [], configErrors} satisfies GraphQLWorkerResult)\n return\n }\n throw err\n }\n\n // Resolve which GraphQL APIs to deploy from workspace + CLI config\n const resolvedApis = resolveGraphQLApis({cliConfig, workspaces})\n\n // Get createSchema from sanity (0ms — already cached by ViteNodeRunner)\n const {createSchema} = await deps.resolveLocalPackage<typeof import('sanity')>('sanity', workDir)\n\n // Build default schema to identify built-in types that should be filtered out\n const defaultSchema = createSchema({name: 'default', types: []})\n const defaultTypes = defaultSchema.getTypeNames()\n const isCustomType = (type: SchemaDefinitionish) => !defaultTypes.includes(type.name)\n\n // For each API: create compiled schema, extract GraphQL spec, catch SchemaError\n const results: ExtractedGraphQLAPI[] = []\n\n for (const api of resolvedApis) {\n const apiBase: Omit<ExtractedGraphQLAPI, 'extracted' | 'extractionError' | 'schemaErrors'> = {\n dataset: api.dataset,\n filterSuffix: api.filterSuffix,\n generation: api.generation,\n id: api.id,\n nonNullDocumentFields: api.nonNullDocumentFields,\n playground: api.playground,\n projectId: api.projectId,\n tag: api.tag,\n }\n\n try {\n const schema = createSchema({\n name: 'default',\n types: api.schemaTypes.filter((type) => isCustomType(type)),\n })\n\n const extracted = extractFromSanitySchema(schema, {\n nonNullDocumentFields:\n nonNullDocumentFieldsFlag === undefined\n ? api.nonNullDocumentFields\n : nonNullDocumentFieldsFlag,\n withUnionCache,\n })\n\n results.push({...apiBase, extracted})\n } catch (err) {\n if (err instanceof SchemaError) {\n results.push({...apiBase, schemaErrors: err.problemGroups})\n } else if (isSchemaError(err)) {\n // Sanity's internal schema error from createSchema() — different class from our\n // SchemaError, but carries structured validation data on err.schema._validation.\n // This is low-probability since getStudioWorkspaces() above already validated,\n // but createSchema() on filtered types could still surface issues.\n const validation = err.schema._validation ?? []\n const errorGroups = validation\n .map((g) => ({...g, problems: g.problems.filter((p) => p.severity === 'error')}))\n .filter((g) => g.problems.length > 0)\n if (errorGroups.length > 0) {\n results.push({...apiBase, schemaErrors: errorGroups})\n } else {\n // Warning-only or empty _validation — fall through to generic error with the\n // message, matching the global path which re-throws warning-only errors.\n results.push({\n ...apiBase,\n extractionError: err instanceof Error ? err.message : String(err),\n })\n }\n } else {\n debug(\n 'Schema extraction failed for %s/%s: %O',\n apiBase.dataset,\n apiBase.tag ?? 'default',\n err,\n )\n results.push({\n ...apiBase,\n extractionError: err instanceof Error ? err.message : String(err),\n })\n }\n }\n }\n\n port.postMessage({apis: results} satisfies GraphQLWorkerResult)\n}\n"],"names":["isMainThread","findStudioConfigPath","getCliConfig","studioWorkerTask","subdebug","isSchemaError","extractFromSanitySchema","resolveGraphQLApis","SchemaError","extractGraphQLAPIs","workDir","options","Error","cliConfig","configPath","Promise","all","result","URL","url","name","studioRootPath","workerData","extractGraphQLConfig","nonNullDocumentFieldsFlag","withUnionCache","configErrors","length","apis","config","graphql","debug","extractGraphQLAPIsWorker","port","data","deps","workspaces","getStudioWorkspaces","err","validation","schema","_validation","map","g","problems","filter","p","severity","postMessage","resolvedApis","createSchema","resolveLocalPackage","defaultSchema","types","defaultTypes","getTypeNames","isCustomType","type","includes","results","api","apiBase","dataset","filterSuffix","generation","id","nonNullDocumentFields","playground","projectId","tag","schemaTypes","extracted","undefined","push","schemaErrors","problemGroups","errorGroups","extractionError","message","String"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/graphql/extractGraphQLAPIs.ts"],"sourcesContent":["import {isMainThread, type MessagePort} from 'node:worker_threads'\n\nimport {findStudioConfigPath, getCliConfig} from '@sanity/cli-core/config'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {studioWorkerTask} from '@sanity/cli-core/tasks'\nimport {type CliConfig} from '@sanity/cli-core/types'\n\nimport {isSchemaError} from '../../util/isSchemaError.js'\nimport {extractFromSanitySchema} from './extractFromSanitySchema.js'\nimport {resolveGraphQLApis, type Workspace} from './resolveGraphQLApisFromWorkspaces.js'\nimport {SchemaError} from './SchemaError.js'\nimport {\n type ExtractedGraphQLAPI,\n type GraphQLAPIConfig,\n type GraphQLWorkerResult,\n type SchemaDefinitionish,\n} from './types.js'\n\n// ---------------------------------------------------------------------------\n// Main-thread orchestrator\n// ---------------------------------------------------------------------------\n\nexport async function extractGraphQLAPIs(\n workDir: string,\n options: {nonNullDocumentFieldsFlag?: boolean; withUnionCache?: boolean},\n): Promise<ExtractedGraphQLAPI[]> {\n if (!isMainThread) {\n throw new Error('extractGraphQLAPIs() must be called from the main thread')\n }\n\n const [cliConfig, configPath] = await Promise.all([\n getCliConfig(workDir),\n findStudioConfigPath(workDir),\n ])\n\n const result = await studioWorkerTask<GraphQLWorkerResult>(\n new URL('extractGraphQLAPIs.worker.js', import.meta.url),\n {\n name: 'extractGraphQLAPIs',\n studioRootPath: workDir,\n workerData: {\n cliConfig: extractGraphQLConfig(cliConfig),\n configPath,\n nonNullDocumentFieldsFlag: options.nonNullDocumentFieldsFlag,\n withUnionCache: options.withUnionCache,\n workDir,\n },\n },\n )\n\n if (result.configErrors?.length) {\n throw new SchemaError(result.configErrors)\n }\n\n return result.apis\n}\n\nfunction extractGraphQLConfig(config: CliConfig) {\n return {\n graphql: config.graphql,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Worker logic (runs inside the worker thread)\n// ---------------------------------------------------------------------------\n\nexport interface ExtractWorkerData {\n configPath: string\n workDir: string\n\n cliConfig?: {graphql?: GraphQLAPIConfig[]}\n nonNullDocumentFieldsFlag?: boolean\n withUnionCache?: boolean\n}\n\nexport interface ExtractWorkerDeps {\n getStudioWorkspaces: (configPath: string) => Promise<unknown[]>\n resolveLocalPackage: <T>(name: string, workDir: string) => Promise<T>\n}\n\nconst debug = subdebug('graphql:extractGraphQLAPIs:worker')\n\nexport async function extractGraphQLAPIsWorker(\n port: MessagePort,\n data: ExtractWorkerData,\n deps: ExtractWorkerDeps,\n): Promise<void> {\n const {cliConfig, configPath, nonNullDocumentFieldsFlag, withUnionCache, workDir} = data\n\n // Load workspaces — this loads sanity.config.ts through Vite, caching `sanity` in the process\n let workspaces: Workspace[]\n try {\n workspaces = (await deps.getStudioWorkspaces(configPath)) as Workspace[]\n } catch (err) {\n if (isSchemaError(err)) {\n const validation = err.schema._validation ?? []\n const configErrors = validation\n .map((g) => ({...g, problems: g.problems.filter((p) => p.severity === 'error')}))\n .filter((g) => g.problems.length > 0)\n\n // Only treat error-severity problems as schema errors. If the validation\n // only contains warnings, re-throw the original error so it isn't silently\n // swallowed — warnings alone should not block deployment.\n if (configErrors.length === 0) {\n throw err\n }\n\n port.postMessage({apis: [], configErrors} satisfies GraphQLWorkerResult)\n return\n }\n throw err\n }\n\n // Resolve which GraphQL APIs to deploy from workspace + CLI config\n const resolvedApis = resolveGraphQLApis({cliConfig, workspaces})\n\n // Get createSchema from sanity (0ms — already cached by ViteNodeRunner)\n const {createSchema} = await deps.resolveLocalPackage<typeof import('sanity')>('sanity', workDir)\n\n // Build default schema to identify built-in types that should be filtered out\n const defaultSchema = createSchema({name: 'default', types: []})\n const defaultTypes = defaultSchema.getTypeNames()\n const isCustomType = (type: SchemaDefinitionish) => !defaultTypes.includes(type.name)\n\n // For each API: create compiled schema, extract GraphQL spec, catch SchemaError\n const results: ExtractedGraphQLAPI[] = []\n\n for (const api of resolvedApis) {\n const apiBase: Omit<ExtractedGraphQLAPI, 'extracted' | 'extractionError' | 'schemaErrors'> = {\n dataset: api.dataset,\n filterSuffix: api.filterSuffix,\n generation: api.generation,\n id: api.id,\n nonNullDocumentFields: api.nonNullDocumentFields,\n playground: api.playground,\n projectId: api.projectId,\n tag: api.tag,\n }\n\n try {\n const schema = createSchema({\n name: 'default',\n types: api.schemaTypes.filter((type) => isCustomType(type)),\n })\n\n const extracted = extractFromSanitySchema(schema, {\n nonNullDocumentFields:\n nonNullDocumentFieldsFlag === undefined\n ? api.nonNullDocumentFields\n : nonNullDocumentFieldsFlag,\n withUnionCache,\n })\n\n results.push({...apiBase, extracted})\n } catch (err) {\n if (err instanceof SchemaError) {\n results.push({...apiBase, schemaErrors: err.problemGroups})\n } else if (isSchemaError(err)) {\n // Sanity's internal schema error from createSchema() — different class from our\n // SchemaError, but carries structured validation data on err.schema._validation.\n // This is low-probability since getStudioWorkspaces() above already validated,\n // but createSchema() on filtered types could still surface issues.\n const validation = err.schema._validation ?? []\n const errorGroups = validation\n .map((g) => ({...g, problems: g.problems.filter((p) => p.severity === 'error')}))\n .filter((g) => g.problems.length > 0)\n if (errorGroups.length > 0) {\n results.push({...apiBase, schemaErrors: errorGroups})\n } else {\n // Warning-only or empty _validation — fall through to generic error with the\n // message, matching the global path which re-throws warning-only errors.\n results.push({\n ...apiBase,\n extractionError: err instanceof Error ? err.message : String(err),\n })\n }\n } else {\n debug(\n 'Schema extraction failed for %s/%s: %O',\n apiBase.dataset,\n apiBase.tag ?? 'default',\n err,\n )\n results.push({\n ...apiBase,\n extractionError: err instanceof Error ? err.message : String(err),\n })\n }\n }\n }\n\n port.postMessage({apis: results} satisfies GraphQLWorkerResult)\n}\n"],"names":["isMainThread","findStudioConfigPath","getCliConfig","subdebug","studioWorkerTask","isSchemaError","extractFromSanitySchema","resolveGraphQLApis","SchemaError","extractGraphQLAPIs","workDir","options","Error","cliConfig","configPath","Promise","all","result","URL","url","name","studioRootPath","workerData","extractGraphQLConfig","nonNullDocumentFieldsFlag","withUnionCache","configErrors","length","apis","config","graphql","debug","extractGraphQLAPIsWorker","port","data","deps","workspaces","getStudioWorkspaces","err","validation","schema","_validation","map","g","problems","filter","p","severity","postMessage","resolvedApis","createSchema","resolveLocalPackage","defaultSchema","types","defaultTypes","getTypeNames","isCustomType","type","includes","results","api","apiBase","dataset","filterSuffix","generation","id","nonNullDocumentFields","playground","projectId","tag","schemaTypes","extracted","undefined","push","schemaErrors","problemGroups","errorGroups","extractionError","message","String"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { relative, resolve } from 'node:path';
|
|
3
3
|
import { doImport, getCliConfigUncached } from '@sanity/cli-core';
|
|
4
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
4
5
|
import { spinner } from '@sanity/cli-core/ux';
|
|
5
|
-
import { getErrorMessage } from '../../util/getErrorMessage.js';
|
|
6
6
|
import { coreAppManifestSchema } from './types.js';
|
|
7
7
|
/**
|
|
8
8
|
* The title change a deploy would sync from the manifest to the user
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/manifest/extractCoreAppManifest.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport {relative, resolve} from 'node:path'\n\nimport {doImport, getCliConfigUncached} from '@sanity/cli-core'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/manifest/extractCoreAppManifest.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport {relative, resolve} from 'node:path'\n\nimport {doImport, getCliConfigUncached} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {type sanitizeIcon as sanitizeIconFn} from './sanitizeIcon.js'\nimport {type CoreAppManifest, coreAppManifestSchema} from './types.js'\n\ninterface ExtractCoreAppManifestOptions {\n workDir: string\n}\n\n/**\n * The title change a deploy would sync from the manifest to the user\n * application, or null when the titles already match (or none is set).\n *\n * @internal\n */\nexport function resolveTitleUpdate(\n manifest: CoreAppManifest | undefined,\n application: {title: string | null},\n): {from: string | null; to: string} | null {\n if (manifest?.title === undefined || manifest.title === application.title) {\n return null\n }\n return {from: application.title, to: manifest.title}\n}\n\nconst sanitizeIconPath = new URL('sanitizeIcon.js', import.meta.url).href\n\n/**\n * Lazy-load {@link sanitizeIconFn} so `isomorphic-dompurify` (and its jsdom\n * dependency) stays out of the CLI's eager import graph. The studio manifest\n * resolver lazy-loads its icon machinery for the same reason; this path runs in\n * the main process (not the manifest worker), so only an app deploy that\n * actually has an icon pays the cost.\n */\nasync function lazySanitizeIcon(): Promise<typeof sanitizeIconFn> {\n const mod = await doImport(sanitizeIconPath)\n return mod.sanitizeIcon\n}\n\n/**\n * Resolves app.icon from config (a file path) to an SVG string for the manifest.\n * The manifest expects the SVG string inline, not a path.\n *\n * The file is sanitized through the same allowlist as the studio manifest's\n * icon resolver (see {@link lazySanitizeIcon}) so both manifest paths inline the\n * same trusted subset of SVG markup.\n */\nasync function readIconFromPath(workDir: string, iconPath: string): Promise<string> {\n const resolvedPath = resolve(workDir, iconPath)\n const pathRelativeToWorkDir = relative(workDir, resolvedPath)\n if (pathRelativeToWorkDir.startsWith('..')) {\n throw new Error(\n `Icon path \"${iconPath}\" resolves outside the project directory and is not allowed.`,\n )\n }\n\n let content: string\n try {\n content = await readFile(resolvedPath, 'utf8')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(\n `Could not read icon file at \"${iconPath}\" (resolved: ${resolvedPath}): ${message}`,\n {cause: err},\n )\n }\n\n const trimmed = content.trim()\n if (!/<svg[\\s>]/i.test(trimmed)) {\n throw new Error(\n `Icon file at \"${iconPath}\" does not contain an SVG element. App manifest icons must be SVG files.`,\n )\n }\n\n const sanitizeIcon = await lazySanitizeIcon()\n return sanitizeIcon(trimmed)\n}\n\n/**\n * Unlike studio manifest extraction, skips schema/tool parsing. The config's\n * `app.icon` is a file path; its content is read and inlined in the manifest.\n */\nexport async function extractCoreAppManifest(\n options: ExtractCoreAppManifestOptions,\n): Promise<CoreAppManifest | undefined> {\n const {workDir} = options\n const {app} = await getCliConfigUncached(workDir)\n if (!app) {\n return undefined\n }\n\n const spin = spinner('Extracting manifest').start()\n\n try {\n let icon: string | undefined\n if (app.icon) {\n icon = await readIconFromPath(workDir, app.icon)\n }\n\n if (!icon && !app.title) {\n spin.info('Manifest creation skipped: no icon or title found in app configuration')\n return undefined\n }\n\n const manifest: CoreAppManifest = coreAppManifestSchema.parse({\n version: '1',\n ...(icon ? {icon} : {}),\n ...(app.title ? {title: app.title} : {}),\n ...(app.group ? {group: app.group} : {}),\n ...(app.priority === undefined ? {} : {priority: app.priority}),\n })\n\n spin.succeed(`Extracted manifest`)\n\n return manifest\n } catch (err) {\n const message = getErrorMessage(err)\n spin.fail(message)\n throw err\n }\n}\n"],"names":["readFile","relative","resolve","doImport","getCliConfigUncached","getErrorMessage","spinner","coreAppManifestSchema","resolveTitleUpdate","manifest","application","title","undefined","from","to","sanitizeIconPath","URL","url","href","lazySanitizeIcon","mod","sanitizeIcon","readIconFromPath","workDir","iconPath","resolvedPath","pathRelativeToWorkDir","startsWith","Error","content","err","message","String","cause","trimmed","trim","test","extractCoreAppManifest","options","app","spin","start","icon","info","parse","version","group","priority","succeed","fail"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { subdebug } from '@sanity/cli-core';
|
|
1
|
+
import { subdebug } from '@sanity/cli-core/debug';
|
|
2
2
|
import { select, spinner } from '@sanity/cli-core/ux';
|
|
3
3
|
import { promptForOrganizationName } from '../../prompts/promptForOrganizationName.js';
|
|
4
4
|
import { createOrganization, listOrganizations } from '../../services/organizations.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/organizations/getOrganization.ts"],"sourcesContent":["import {Output, type SanityOrgUser
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/organizations/getOrganization.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core/debug'\nimport {type Output, type SanityOrgUser} from '@sanity/cli-core/types'\nimport {select, spinner} from '@sanity/cli-core/ux'\n\nimport {promptForOrganizationName} from '../../prompts/promptForOrganizationName.js'\nimport {\n createOrganization,\n listOrganizations,\n type ProjectOrganization,\n} from '../../services/organizations.js'\nimport {findOrganizationByUserName} from './findOrganizationByUserName.js'\nimport {getOrganizationChoices} from './getOrganizationChoices.js'\nimport {getOrganizationsWithAttachGrantInfo} from './getOrganizationsWithAttachGrantInfo.js'\n\nconst debug = subdebug('getOrganizationId')\n\ninterface GetOrganizationOptions {\n isUnattended: boolean\n output: Output\n requestedId: string | undefined\n user: SanityOrgUser\n}\n\nconst promptAndCreateNewOrganization = async (user: SanityOrgUser) => {\n const organizationName = await promptForOrganizationName(user)\n const spin = spinner('Creating organization').start()\n const newOrganization = await createOrganization(organizationName)\n spin.succeed()\n return newOrganization\n}\n\nexport async function getOrganization({\n isUnattended,\n output,\n requestedId,\n user,\n}: GetOrganizationOptions) {\n // Get available organizations\n const spin = spinner('Loading organizations').start()\n let organizations: ProjectOrganization[]\n try {\n organizations = await listOrganizations()\n spin.succeed()\n } catch (error) {\n spin.fail()\n debug('Error retrieving organization list', error)\n throw error\n }\n\n // If organization is specified, validate it\n if (requestedId) {\n const org = organizations.find((o) => o.id === requestedId || o.slug === requestedId)\n if (org) return org\n\n throw new Error(`Organization \"${requestedId}\" not found or you don't have access to it`)\n }\n\n // If the user has no organizations, prompt them to create one with the same name as\n // their user, but allow them to customize it if they want\n if (organizations.length === 0) {\n output.log('You need to create an organization to create projects.')\n return promptAndCreateNewOrganization(user)\n }\n\n // If the user has organizations, let them choose from them, but also allow them to\n // create a new one in case they do not have access to any of them, or they want to\n // create a personal/other organization.\n debug(`User has ${organizations.length} organization(s), checking attach access`)\n const withGrantInfo = await getOrganizationsWithAttachGrantInfo(organizations)\n const withAttach = withGrantInfo.filter(({hasAttachGrant}) => hasAttachGrant)\n\n debug('User has attach access to %d organizations.', withAttach.length)\n const organizationChoices = getOrganizationChoices(withGrantInfo)\n\n // In unattended mode use defaults without prompting\n if (isUnattended) {\n // Use the first organization with attach permissions\n return withAttach.length > 0 ? withAttach[0].organization : undefined\n }\n\n // If the user only has a single organization (and they have attach access to it),\n // we'll default to that one. Otherwise, we'll default to the organization with the\n // same name as the user if it exists.\n const defaultOrganizationId =\n withAttach.length === 1\n ? withAttach[0].organization.id\n : findOrganizationByUserName(organizations, user)\n\n const chosenOrg = await select({\n choices: organizationChoices,\n default: defaultOrganizationId || undefined,\n message: 'Select organization:',\n })\n\n if (chosenOrg === '-new-') {\n return promptAndCreateNewOrganization(user)\n }\n\n return organizations.find((org) => org.id === chosenOrg)\n}\n"],"names":["subdebug","select","spinner","promptForOrganizationName","createOrganization","listOrganizations","findOrganizationByUserName","getOrganizationChoices","getOrganizationsWithAttachGrantInfo","debug","promptAndCreateNewOrganization","user","organizationName","spin","start","newOrganization","succeed","getOrganization","isUnattended","output","requestedId","organizations","error","fail","org","find","o","id","slug","Error","length","log","withGrantInfo","withAttach","filter","hasAttachGrant","organizationChoices","organization","undefined","defaultOrganizationId","chosenOrg","choices","default","message"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,yBAAwB;AAE/C,SAAQC,MAAM,EAAEC,OAAO,QAAO,sBAAqB;AAEnD,SAAQC,yBAAyB,QAAO,6CAA4C;AACpF,SACEC,kBAAkB,EAClBC,iBAAiB,QAEZ,kCAAiC;AACxC,SAAQC,0BAA0B,QAAO,kCAAiC;AAC1E,SAAQC,sBAAsB,QAAO,8BAA6B;AAClE,SAAQC,mCAAmC,QAAO,2CAA0C;AAE5F,MAAMC,QAAQT,SAAS;AASvB,MAAMU,iCAAiC,OAAOC;IAC5C,MAAMC,mBAAmB,MAAMT,0BAA0BQ;IACzD,MAAME,OAAOX,QAAQ,yBAAyBY,KAAK;IACnD,MAAMC,kBAAkB,MAAMX,mBAAmBQ;IACjDC,KAAKG,OAAO;IACZ,OAAOD;AACT;AAEA,OAAO,eAAeE,gBAAgB,EACpCC,YAAY,EACZC,MAAM,EACNC,WAAW,EACXT,IAAI,EACmB;IACvB,8BAA8B;IAC9B,MAAME,OAAOX,QAAQ,yBAAyBY,KAAK;IACnD,IAAIO;IACJ,IAAI;QACFA,gBAAgB,MAAMhB;QACtBQ,KAAKG,OAAO;IACd,EAAE,OAAOM,OAAO;QACdT,KAAKU,IAAI;QACTd,MAAM,sCAAsCa;QAC5C,MAAMA;IACR;IAEA,4CAA4C;IAC5C,IAAIF,aAAa;QACf,MAAMI,MAAMH,cAAcI,IAAI,CAAC,CAACC,IAAMA,EAAEC,EAAE,KAAKP,eAAeM,EAAEE,IAAI,KAAKR;QACzE,IAAII,KAAK,OAAOA;QAEhB,MAAM,IAAIK,MAAM,CAAC,cAAc,EAAET,YAAY,0CAA0C,CAAC;IAC1F;IAEA,oFAAoF;IACpF,0DAA0D;IAC1D,IAAIC,cAAcS,MAAM,KAAK,GAAG;QAC9BX,OAAOY,GAAG,CAAC;QACX,OAAOrB,+BAA+BC;IACxC;IAEA,mFAAmF;IACnF,mFAAmF;IACnF,wCAAwC;IACxCF,MAAM,CAAC,SAAS,EAAEY,cAAcS,MAAM,CAAC,wCAAwC,CAAC;IAChF,MAAME,gBAAgB,MAAMxB,oCAAoCa;IAChE,MAAMY,aAAaD,cAAcE,MAAM,CAAC,CAAC,EAACC,cAAc,EAAC,GAAKA;IAE9D1B,MAAM,+CAA+CwB,WAAWH,MAAM;IACtE,MAAMM,sBAAsB7B,uBAAuByB;IAEnD,qDAAqD;IACrD,IAAId,cAAc;QAChB,qDAAqD;QACrD,OAAOe,WAAWH,MAAM,GAAG,IAAIG,UAAU,CAAC,EAAE,CAACI,YAAY,GAAGC;IAC9D;IAEA,kFAAkF;IAClF,mFAAmF;IACnF,sCAAsC;IACtC,MAAMC,wBACJN,WAAWH,MAAM,KAAK,IAClBG,UAAU,CAAC,EAAE,CAACI,YAAY,CAACV,EAAE,GAC7BrB,2BAA2Be,eAAeV;IAEhD,MAAM6B,YAAY,MAAMvC,OAAO;QAC7BwC,SAASL;QACTM,SAASH,yBAAyBD;QAClCK,SAAS;IACX;IAEA,IAAIH,cAAc,SAAS;QACzB,OAAO9B,+BAA+BC;IACxC;IAEA,OAAOU,cAAcI,IAAI,CAAC,CAACD,MAAQA,IAAIG,EAAE,KAAKa;AAChD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { writeFileSync } from 'node:fs';
|
|
2
2
|
import { formatSchemaValidation, getAggregatedSeverity } from '@sanity/cli-build/_internal/extract';
|
|
3
|
-
import { studioWorkerTask } from '@sanity/cli-core';
|
|
3
|
+
import { studioWorkerTask } from '@sanity/cli-core/tasks';
|
|
4
4
|
import { logSymbols, spinner } from '@sanity/cli-core/ux';
|
|
5
5
|
import { generateMetafile } from './metafile.js';
|
|
6
6
|
export async function validateAction(options) {
|