@sanity/cli 8.23.0 → 8.25.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 +401 -87
- package/bin/run.js +7 -1
- package/dist/actions/codemods/dashboard.js +136 -8
- package/dist/actions/codemods/dashboard.js.map +1 -1
- package/dist/actions/codemods/types.js.map +1 -1
- package/dist/actions/dev/devAction.js +48 -44
- package/dist/actions/dev/devAction.js.map +1 -1
- package/dist/actions/init/initHelpers.js +1 -1
- package/dist/actions/init/initHelpers.js.map +1 -1
- package/dist/actions/init/initStudio.js +1 -1
- package/dist/actions/init/initStudio.js.map +1 -1
- package/dist/actions/init/project/getOrCreateDataset.js +10 -12
- package/dist/actions/init/project/getOrCreateDataset.js.map +1 -1
- package/dist/actions/init/project/getOrCreateProject.js +7 -5
- package/dist/actions/init/project/getOrCreateProject.js.map +1 -1
- package/dist/actions/init/project/promptUserForOrganization.js +6 -4
- package/dist/actions/init/project/promptUserForOrganization.js.map +1 -1
- package/dist/commands/api.js +1 -1
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/assets/upload.js +1 -1
- package/dist/commands/assets/upload.js.map +1 -1
- package/dist/commands/codemod.js +1 -0
- package/dist/commands/codemod.js.map +1 -1
- package/dist/commands/cors/add.js +1 -1
- package/dist/commands/cors/add.js.map +1 -1
- package/dist/commands/cors/delete.js +1 -1
- package/dist/commands/cors/delete.js.map +1 -1
- package/dist/commands/cors/list.js +1 -1
- package/dist/commands/cors/list.js.map +1 -1
- package/dist/commands/datasets/alias/create.js +1 -1
- package/dist/commands/datasets/alias/create.js.map +1 -1
- package/dist/commands/datasets/import.js +0 -8
- package/dist/commands/datasets/import.js.map +1 -1
- package/dist/commands/datasets/list.js +1 -1
- package/dist/commands/datasets/list.js.map +1 -1
- package/dist/commands/documents/create.js +1 -1
- package/dist/commands/documents/create.js.map +1 -1
- package/dist/commands/documents/delete.js +1 -1
- package/dist/commands/documents/delete.js.map +1 -1
- package/dist/commands/documents/get.js +1 -1
- package/dist/commands/documents/get.js.map +1 -1
- package/dist/commands/documents/query.js +1 -9
- package/dist/commands/documents/query.js.map +1 -1
- package/dist/commands/documents/validate.js +2 -2
- package/dist/commands/documents/validate.js.map +1 -1
- package/dist/commands/graphql/list.js +1 -1
- package/dist/commands/graphql/list.js.map +1 -1
- package/dist/commands/graphql/undeploy.js +1 -11
- package/dist/commands/graphql/undeploy.js.map +1 -1
- package/dist/commands/hooks/attempt.js +1 -1
- package/dist/commands/hooks/attempt.js.map +1 -1
- package/dist/commands/hooks/create.js +1 -1
- package/dist/commands/hooks/create.js.map +1 -1
- package/dist/commands/hooks/delete.js +1 -1
- package/dist/commands/hooks/delete.js.map +1 -1
- package/dist/commands/hooks/list.js +1 -1
- package/dist/commands/hooks/list.js.map +1 -1
- package/dist/commands/hooks/logs.js +1 -1
- package/dist/commands/hooks/logs.js.map +1 -1
- package/dist/commands/init.js +3 -5
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrations/run.js +5 -5
- package/dist/commands/migrations/run.js.map +1 -1
- package/dist/commands/projects/unclaimed.js +6 -5
- package/dist/commands/projects/unclaimed.js.map +1 -1
- package/dist/commands/tokens/create.js +4 -4
- package/dist/commands/tokens/create.js.map +1 -1
- package/dist/commands/tokens/delete.js +2 -2
- package/dist/commands/tokens/delete.js.map +1 -1
- package/dist/commands/tokens/list.js +2 -2
- package/dist/commands/tokens/list.js.map +1 -1
- package/dist/commands/users/invite.js +1 -1
- package/dist/commands/users/invite.js.map +1 -1
- package/dist/commands/users/list.js +1 -1
- package/dist/commands/users/list.js.map +1 -1
- package/dist/exports/invokeSanityCli/index.js +1 -1
- package/dist/exports/invokeSanityCli/index.js.map +1 -1
- package/dist/hooks/prerun/injectEnvVariables.js +1 -1
- package/dist/hooks/prerun/injectEnvVariables.js.map +1 -1
- package/dist/util/searchChoices.js +52 -0
- package/dist/util/searchChoices.js.map +1 -0
- package/dist/util/sharedFlags.js +8 -4
- package/dist/util/sharedFlags.js.map +1 -1
- package/dist/util/warnOnNonProductionEnvironment.js +4 -3
- package/dist/util/warnOnNonProductionEnvironment.js.map +1 -1
- package/oclif.manifest.json +515 -351
- package/package.json +8 -8
- package/dist/util/getSanityEnv.js +0 -8
- package/dist/util/getSanityEnv.js.map +0 -1
- package/templates/shopify/.gitignore +0 -26
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/init/project/getOrCreateDataset.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/init/project/getOrCreateDataset.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\nimport {search, Separator} from '@sanity/cli-core/ux'\n\nimport {promptForDatasetName} from '../../../prompts/promptForDatasetName.js'\nimport {promptForDefaultConfig} from '../../../prompts/promptForDefaultConfig.js'\nimport {listDatasets} from '../../../services/datasets.js'\nimport {getProjectFeatures} from '../../../services/getProjectFeatures.js'\nimport {searchChoices, searchTheme} from '../../../util/searchChoices.js'\nimport {createDataset} from '../../dataset/create.js'\nimport {type InitContext} from '../types.js'\n\nconst debug = subdebug('init')\n\nexport async function getOrCreateDataset(opts: {\n dataset?: string\n defaultConfig: boolean | undefined\n displayName: string\n output: InitContext['output']\n projectId: string\n showDefaultConfigPrompt: boolean\n unattended: boolean\n visibility?: string\n}): Promise<{\n datasetName: string\n userAction: 'create' | 'none' | 'select'\n}> {\n const {dataset, visibility} = opts\n let {defaultConfig} = opts\n\n const [datasets, projectFeatures] = await Promise.all([\n listDatasets(opts.projectId),\n getProjectFeatures(opts.projectId),\n ])\n\n if (dataset) {\n debug('User has specified dataset through a flag (%s)', dataset)\n const existing = datasets.find((ds) => ds.name === dataset)\n if (!existing) {\n debug('Specified dataset not found, creating it')\n await createDataset({\n datasetName: dataset,\n forcePublic: defaultConfig,\n isUnattended: opts.unattended,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n }\n\n return {datasetName: dataset, userAction: existing ? 'none' : 'create'}\n }\n\n if (opts.unattended) {\n debug('Unattended mode without --dataset, defaulting to \"production\" dataset')\n const datasetName = 'production'\n const existing = datasets.find((ds) => ds.name === datasetName)\n if (!existing) {\n await createDataset({\n datasetName,\n forcePublic: visibility === undefined,\n isUnattended: true,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n }\n return {datasetName, userAction: existing ? 'none' : 'create'}\n }\n\n if (datasets.length === 0) {\n debug('No datasets found for project, prompting for name')\n if (opts.showDefaultConfigPrompt) {\n defaultConfig = await promptForDefaultConfig()\n }\n const name = defaultConfig\n ? 'production'\n : await promptForDatasetName({\n message: 'Name of your first dataset:',\n })\n await createDataset({\n datasetName: name,\n forcePublic: defaultConfig,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n return {datasetName: name, userAction: 'create'}\n }\n\n debug(`User has ${datasets.length} dataset(s) already, showing list of choices`)\n const datasetChoices = datasets.map((dataset) => ({name: dataset.name, value: dataset.name}))\n\n const selected = await search({\n message: 'Select dataset to use',\n source: searchChoices([\n {name: 'Create new dataset', value: 'new'},\n new Separator(),\n ...datasetChoices,\n ]),\n theme: searchTheme,\n })\n\n if (selected === 'new') {\n const existingDatasetNames = datasets.map((ds) => ds.name)\n debug('User wants to create a new dataset, prompting for name')\n if (opts.showDefaultConfigPrompt && !existingDatasetNames.includes('production')) {\n defaultConfig = await promptForDefaultConfig()\n }\n\n const newDatasetName = defaultConfig\n ? 'production'\n : await promptForDatasetName(\n {\n message: 'Dataset name:',\n },\n existingDatasetNames,\n )\n await createDataset({\n datasetName: newDatasetName,\n forcePublic: defaultConfig,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n return {datasetName: newDatasetName, userAction: 'create'}\n }\n\n debug(`Returning selected dataset (${selected})`)\n return {datasetName: selected, userAction: 'select'}\n}\n"],"names":["subdebug","search","Separator","promptForDatasetName","promptForDefaultConfig","listDatasets","getProjectFeatures","searchChoices","searchTheme","createDataset","debug","getOrCreateDataset","opts","dataset","visibility","defaultConfig","datasets","projectFeatures","Promise","all","projectId","existing","find","ds","name","datasetName","forcePublic","isUnattended","unattended","output","userAction","undefined","length","showDefaultConfigPrompt","message","datasetChoices","map","value","selected","source","theme","existingDatasetNames","includes","newDatasetName"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { subdebug } from '@sanity/cli-core';
|
|
2
|
-
import {
|
|
2
|
+
import { search, Separator } from '@sanity/cli-core/ux';
|
|
3
3
|
import { listOrganizations } from '../../../services/organizations.js';
|
|
4
4
|
import { listProjects } from '../../../services/projects.js';
|
|
5
|
+
import { searchChoices, searchTheme } from '../../../util/searchChoices.js';
|
|
5
6
|
import { hasProjectAttachGrant } from '../../organizations/hasProjectAttachGrant.js';
|
|
6
7
|
import { InitError } from '../initError.js';
|
|
7
8
|
import { promptForProjectCreation } from './promptForProjectCreation.js';
|
|
@@ -87,16 +88,17 @@ export async function getOrCreateProject({ coupon, newProject, organization, pla
|
|
|
87
88
|
name: `${project.displayName} (${project.id})`,
|
|
88
89
|
value: project.id
|
|
89
90
|
}));
|
|
90
|
-
const selected = await
|
|
91
|
-
|
|
91
|
+
const selected = await search({
|
|
92
|
+
message: 'Create a new project or select an existing one',
|
|
93
|
+
source: searchChoices([
|
|
92
94
|
{
|
|
93
95
|
name: 'Create new project',
|
|
94
96
|
value: 'new'
|
|
95
97
|
},
|
|
96
98
|
new Separator(),
|
|
97
99
|
...projectChoices
|
|
98
|
-
],
|
|
99
|
-
|
|
100
|
+
]),
|
|
101
|
+
theme: searchTheme
|
|
100
102
|
});
|
|
101
103
|
if (selected === 'new') {
|
|
102
104
|
debug('User wants to create a new project, prompting for name');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/init/project/getOrCreateProject.ts"],"sourcesContent":["import {type SanityOrgUser, subdebug} from '@sanity/cli-core'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/init/project/getOrCreateProject.ts"],"sourcesContent":["import {type SanityOrgUser, subdebug} from '@sanity/cli-core'\nimport {search, Separator} from '@sanity/cli-core/ux'\n\nimport {listOrganizations, type ProjectOrganization} from '../../../services/organizations.js'\nimport {listProjects} from '../../../services/projects.js'\nimport {searchChoices, searchTheme} from '../../../util/searchChoices.js'\nimport {hasProjectAttachGrant} from '../../organizations/hasProjectAttachGrant.js'\nimport {InitError} from '../initError.js'\nimport {promptForProjectCreation} from './promptForProjectCreation.js'\n\nconst debug = subdebug('init')\n\nexport async function getOrCreateProject({\n coupon,\n newProject,\n organization,\n planId,\n project,\n unattended,\n user,\n}: {\n coupon?: string\n newProject: string | undefined\n organization: string | undefined\n planId: string | undefined\n project: string | undefined\n unattended: boolean\n user: SanityOrgUser\n}): Promise<{\n displayName: string\n isFirstProject: boolean\n organizationId?: string\n projectId: string\n userAction: 'create' | 'select'\n}> {\n const projectId = project || newProject\n let projects\n let organizations: ProjectOrganization[]\n\n // When a projectId is provided, organizations aren't needed for the fast path, so\n // skip that request entirely. Only fetch both in parallel when prompting is required.\n try {\n if (projectId) {\n projects = (await listProjects()).toSorted((a, b) => b.createdAt.localeCompare(a.createdAt))\n organizations = []\n } else {\n const [allProjects, allOrgs] = await Promise.all([listProjects(), listOrganizations()])\n projects = allProjects.toSorted((a, b) => b.createdAt.localeCompare(a.createdAt))\n organizations = allOrgs\n }\n } catch (err: unknown) {\n if (unattended && projectId) {\n return {\n displayName: 'Unknown project',\n isFirstProject: false,\n organizationId: organization,\n projectId,\n userAction: 'select',\n }\n }\n const message = err instanceof Error ? err.message : String(err)\n throw new InitError(`Failed to communicate with the Sanity API:\\n${message}`, 1)\n }\n\n if (projects.length === 0 && unattended) {\n throw new InitError('No projects found for current user', 1)\n }\n\n if (projectId) {\n // When called after createProjectFromName (--project-name flow), the new project\n // is expected to appear here since listProjects reads the same user's own projects.\n // If it doesn't, fail loudly rather than continuing with an \"Unknown project\" placeholder.\n const proj = projects.find((p) => p.id === projectId)\n if (!proj) {\n throw new InitError(\n `Given project ID (${projectId}) not found, or you do not have access to it`,\n 1,\n )\n }\n\n return {\n displayName: proj.displayName,\n isFirstProject: false,\n organizationId: proj.organizationId ?? undefined,\n projectId,\n userAction: 'select',\n }\n }\n\n if (organization) {\n const org =\n organizations.find((o) => o.id === organization) ||\n organizations.find((o) => o.slug === organization)\n\n if (!org) {\n throw new InitError(\n `Given organization ID (${organization}) not found, or you do not have access to it`,\n 1,\n )\n }\n\n if (!(await hasProjectAttachGrant(organization))) {\n throw new InitError(\n 'You lack the necessary permissions to attach a project to this organization',\n 1,\n )\n }\n }\n\n const isUsersFirstProject = projects.length === 0\n if (isUsersFirstProject || coupon) {\n debug(\n isUsersFirstProject\n ? 'No projects found for user, prompting for name'\n : 'Using a coupon - skipping project selection',\n )\n\n const created = await promptForProjectCreation({\n coupon,\n isUsersFirstProject,\n organizationId: organization,\n organizations,\n planId,\n user,\n })\n\n return {\n ...created,\n isFirstProject: isUsersFirstProject,\n userAction: 'create',\n }\n }\n\n debug(`User has ${projects.length} project(s) already, showing list of choices`)\n\n const projectChoices = projects.map((project) => ({\n name: `${project.displayName} (${project.id})`,\n value: project.id,\n }))\n\n const selected = await search({\n message: 'Create a new project or select an existing one',\n source: searchChoices([\n {name: 'Create new project', value: 'new'},\n new Separator(),\n ...projectChoices,\n ]),\n theme: searchTheme,\n })\n\n if (selected === 'new') {\n debug('User wants to create a new project, prompting for name')\n\n const created = await promptForProjectCreation({\n coupon,\n isUsersFirstProject,\n organizationId: organization,\n organizations,\n planId,\n user,\n })\n\n return {\n ...created,\n isFirstProject: isUsersFirstProject,\n userAction: 'create',\n }\n }\n\n debug(`Returning selected project (${selected})`)\n const selectedProject = projects.find((proj) => proj.id === selected)\n return {\n displayName: selectedProject?.displayName || '',\n isFirstProject: isUsersFirstProject,\n organizationId: selectedProject?.organizationId ?? undefined,\n projectId: selected,\n userAction: 'select',\n }\n}\n"],"names":["subdebug","search","Separator","listOrganizations","listProjects","searchChoices","searchTheme","hasProjectAttachGrant","InitError","promptForProjectCreation","debug","getOrCreateProject","coupon","newProject","organization","planId","project","unattended","user","projectId","projects","organizations","toSorted","a","b","createdAt","localeCompare","allProjects","allOrgs","Promise","all","err","displayName","isFirstProject","organizationId","userAction","message","Error","String","length","proj","find","p","id","undefined","org","o","slug","isUsersFirstProject","created","projectChoices","map","name","value","selected","source","theme","selectedProject"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { subdebug } from '@sanity/cli-core';
|
|
2
|
-
import {
|
|
2
|
+
import { search } from '@sanity/cli-core/ux';
|
|
3
|
+
import { searchChoices, searchTheme } from '../../../util/searchChoices.js';
|
|
3
4
|
import { findOrganizationByUserName } from '../../organizations/findOrganizationByUserName.js';
|
|
4
5
|
import { getOrganizationChoices } from '../../organizations/getOrganizationChoices.js';
|
|
5
6
|
import { getOrganizationsWithAttachGrantInfo } from '../../organizations/getOrganizationsWithAttachGrantInfo.js';
|
|
@@ -23,10 +24,11 @@ export async function promptUserForOrganization({ isAppTemplate = false, organiz
|
|
|
23
24
|
organizationChoices = getOrganizationChoices(withGrantInfo);
|
|
24
25
|
defaultOrganizationId = withAttach.length === 1 ? withAttach[0].organization.id : findOrganizationByUserName(organizations, user);
|
|
25
26
|
}
|
|
26
|
-
const chosenOrg = await
|
|
27
|
-
choices: organizationChoices,
|
|
27
|
+
const chosenOrg = await search({
|
|
28
28
|
default: defaultOrganizationId || undefined,
|
|
29
|
-
message: 'Select organization:'
|
|
29
|
+
message: 'Select organization:',
|
|
30
|
+
source: searchChoices(organizationChoices),
|
|
31
|
+
theme: searchTheme
|
|
30
32
|
});
|
|
31
33
|
if (chosenOrg === '-new-') {
|
|
32
34
|
const newOrganization = await promptUserForNewOrganization(user);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/init/project/promptUserForOrganization.ts"],"sourcesContent":["import {type SanityOrgUser, subdebug} from '@sanity/cli-core'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/init/project/promptUserForOrganization.ts"],"sourcesContent":["import {type SanityOrgUser, subdebug} from '@sanity/cli-core'\nimport {search} from '@sanity/cli-core/ux'\n\nimport {type ProjectOrganization} from '../../../services/organizations.js'\nimport {searchChoices, searchTheme} from '../../../util/searchChoices.js'\nimport {findOrganizationByUserName} from '../../organizations/findOrganizationByUserName.js'\nimport {getOrganizationChoices} from '../../organizations/getOrganizationChoices.js'\nimport {getOrganizationsWithAttachGrantInfo} from '../../organizations/getOrganizationsWithAttachGrantInfo.js'\nimport {type OrganizationChoices} from '../../organizations/types.js'\nimport {promptUserForNewOrganization} from './promptUserForNewOrganization.js'\n\nconst debug = subdebug('init')\n\nexport async function promptUserForOrganization({\n isAppTemplate = false,\n organizations,\n user,\n}: {\n isAppTemplate?: boolean\n organizations: ProjectOrganization[]\n user: SanityOrgUser\n}) {\n if (organizations.length === 0) {\n const newOrganization = await promptUserForNewOrganization(user)\n return newOrganization.id\n }\n\n let organizationChoices: OrganizationChoices\n let defaultOrganizationId: string | undefined\n\n if (isAppTemplate) {\n organizationChoices = getOrganizationChoices(organizations)\n defaultOrganizationId =\n organizations.length === 1\n ? organizations[0].id\n : findOrganizationByUserName(organizations, user)\n } else {\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 organizationChoices = getOrganizationChoices(withGrantInfo)\n defaultOrganizationId =\n withAttach.length === 1\n ? withAttach[0].organization.id\n : findOrganizationByUserName(organizations, user)\n }\n\n const chosenOrg = await search({\n default: defaultOrganizationId || undefined,\n message: 'Select organization:',\n source: searchChoices(organizationChoices),\n theme: searchTheme,\n })\n\n if (chosenOrg === '-new-') {\n const newOrganization = await promptUserForNewOrganization(user)\n return newOrganization.id\n }\n\n return chosenOrg || undefined\n}\n"],"names":["subdebug","search","searchChoices","searchTheme","findOrganizationByUserName","getOrganizationChoices","getOrganizationsWithAttachGrantInfo","promptUserForNewOrganization","debug","promptUserForOrganization","isAppTemplate","organizations","user","length","newOrganization","id","organizationChoices","defaultOrganizationId","withGrantInfo","withAttach","filter","hasAttachGrant","organization","chosenOrg","default","undefined","message","source","theme"],"mappings":"AAAA,SAA4BA,QAAQ,QAAO,mBAAkB;AAC7D,SAAQC,MAAM,QAAO,sBAAqB;AAG1C,SAAQC,aAAa,EAAEC,WAAW,QAAO,iCAAgC;AACzE,SAAQC,0BAA0B,QAAO,oDAAmD;AAC5F,SAAQC,sBAAsB,QAAO,gDAA+C;AACpF,SAAQC,mCAAmC,QAAO,6DAA4D;AAE9G,SAAQC,4BAA4B,QAAO,oCAAmC;AAE9E,MAAMC,QAAQR,SAAS;AAEvB,OAAO,eAAeS,0BAA0B,EAC9CC,gBAAgB,KAAK,EACrBC,aAAa,EACbC,IAAI,EAKL;IACC,IAAID,cAAcE,MAAM,KAAK,GAAG;QAC9B,MAAMC,kBAAkB,MAAMP,6BAA6BK;QAC3D,OAAOE,gBAAgBC,EAAE;IAC3B;IAEA,IAAIC;IACJ,IAAIC;IAEJ,IAAIP,eAAe;QACjBM,sBAAsBX,uBAAuBM;QAC7CM,wBACEN,cAAcE,MAAM,KAAK,IACrBF,aAAa,CAAC,EAAE,CAACI,EAAE,GACnBX,2BAA2BO,eAAeC;IAClD,OAAO;QACLJ,MAAM,CAAC,SAAS,EAAEG,cAAcE,MAAM,CAAC,wCAAwC,CAAC;QAChF,MAAMK,gBAAgB,MAAMZ,oCAAoCK;QAChE,MAAMQ,aAAaD,cAAcE,MAAM,CAAC,CAAC,EAACC,cAAc,EAAC,GAAKA;QAE9Db,MAAM,+CAA+CW,WAAWN,MAAM;QACtEG,sBAAsBX,uBAAuBa;QAC7CD,wBACEE,WAAWN,MAAM,KAAK,IAClBM,UAAU,CAAC,EAAE,CAACG,YAAY,CAACP,EAAE,GAC7BX,2BAA2BO,eAAeC;IAClD;IAEA,MAAMW,YAAY,MAAMtB,OAAO;QAC7BuB,SAASP,yBAAyBQ;QAClCC,SAAS;QACTC,QAAQzB,cAAcc;QACtBY,OAAOzB;IACT;IAEA,IAAIoB,cAAc,SAAS;QACzB,MAAMT,kBAAkB,MAAMP,6BAA6BK;QAC3D,OAAOE,gBAAgBC,EAAE;IAC3B;IAEA,OAAOQ,aAAaE;AACtB"}
|
package/dist/commands/api.js
CHANGED
|
@@ -261,7 +261,7 @@ specific token instead - for example in CI or when the CLI is not logged in
|
|
|
261
261
|
return resolveEndpoint({
|
|
262
262
|
...baseOptions,
|
|
263
263
|
forceHost,
|
|
264
|
-
projectId: flags
|
|
264
|
+
projectId: flags.project ?? cliConfig.api?.projectId
|
|
265
265
|
});
|
|
266
266
|
} catch (error) {
|
|
267
267
|
if (!(error instanceof ProjectIdRequiredError)) throw error;
|
package/dist/commands/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/commands/api.ts"],"sourcesContent":["import {readFileSync} from 'node:fs'\nimport {readFile} from 'node:fs/promises'\nimport {buffer} from 'node:stream/consumers'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {colorizeJson, exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {ApiUsageError, ProjectIdRequiredError} from '../actions/api/errors.js'\nimport {fieldsToQuery, type FieldValue, parseFields} from '../actions/api/parseFields.js'\nimport {type ResolvedEndpoint, resolveEndpoint} from '../actions/api/resolveEndpoint.js'\nimport {apiRoutes} from '../generated/apiRoutes.js'\nimport {promptForProject} from '../prompts/promptForProject.js'\nimport {type ApiResponse, performApiRequest} from '../services/api.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../util/sharedFlags.js'\n\nconst apiDebug = subdebug('api')\n\nconst METHOD_RE = /^[a-z]+$/i\n\nconst BODYLESS_METHODS = new Set(['GET', 'HEAD'])\n\nexport class ApiCommand extends SanityCommand<typeof ApiCommand> {\n static override args = {\n endpoint: Args.string({\n description:\n 'API path (eg \"projects\" or \"data/query/{dataset}\"), optionally with placeholders, or a full https://*.api.sanity.io URL',\n required: true,\n }),\n }\n\n static override description = `Make an authenticated HTTP request to a Sanity API\n\nThe endpoint argument is an API path as documented in the published OpenAPI\nspecifications - list them with \"sanity openapi list\" and inspect one with\n\"sanity openapi get <slug>\". Paths can be copied verbatim from the specs:\n{projectId} and {dataset} placeholders are filled in from flags or the CLI\nconfiguration, and the API host (api.sanity.io or <projectId>.api.sanity.io)\nis chosen based on the specs' routing information.\n\nThe default request method is GET, or POST when fields or --input are\nprovided. For GET/HEAD requests, fields are sent as query parameters;\notherwise they are combined into a JSON request body sent with\n\"Content-Type: application/json\". Raw --input bodies are sent without a\ndefault Content-Type - provide one with -H when the API requires it. The\nresponse body is written to stdout.\n\nRequests are authenticated with the token from \"sanity login\". To use a\nspecific token instead - for example in CI or when the CLI is not logged in\n- pass --token or set the SANITY_AUTH_TOKEN environment variable. Pass\n--anonymous to send no token at all.`\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> users/me',\n description: 'Get the current user',\n },\n {\n command: '<%= config.bin %> <%= command.id %> projects/{projectId}',\n description: 'Get the current project (placeholder filled from CLI config)',\n },\n {\n command: `<%= config.bin %> <%= command.id %> 'data/query/{dataset}' -f query='*[_type == \"movie\"][0..2]'`,\n description: 'Run a GROQ query against the project host',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> projects/{projectId} -X PATCH -F displayName=\"My project\"',\n description: 'Send a JSON body built from typed fields',\n },\n {\n command: `echo '{\"mutations\": []}' | <%= config.bin %> <%= command.id %> 'data/mutate/{dataset}' --input - -H 'Content-Type: application/json'`,\n description: 'Send a raw request body from stdin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> jobs/123 --include --api-version v2025-02-19',\n description: 'Include the response status and headers, pinning the API version',\n },\n {\n command: 'SANITY_AUTH_TOKEN=<token> <%= config.bin %> <%= command.id %> users/me',\n description: 'Authenticate with a specific token instead of the logged-in session',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID for {projectId} placeholders and project-hosted APIs',\n semantics: 'override',\n }),\n ...getDatasetFlag({\n description: 'Dataset for {dataset} placeholders',\n semantics: 'override',\n }),\n anonymous: Flags.boolean({\n default: false,\n description: 'Send the request without an authorization token',\n }),\n 'api-version': Flags.string({\n description:\n 'API version to use (eg v2025-02-19). Defaults to a version embedded in the endpoint path, or the version from the matching OpenAPI spec',\n helpValue: '<version>',\n }),\n field: Flags.string({\n char: 'F',\n description:\n 'Add a typed parameter (key=value): true/false/null and numbers are converted, @file reads the value from a file, @- from stdin',\n helpValue: '<key=value>',\n multiple: true,\n }),\n global: Flags.boolean({\n description: 'Force the request to the global API host (api.sanity.io)',\n exclusive: ['project-hosted'],\n }),\n header: Flags.string({\n char: 'H',\n description: 'Add an HTTP request header (key: value)',\n helpValue: '<key:value>',\n multiple: true,\n }),\n include: Flags.boolean({\n char: 'i',\n default: false,\n description: 'Include the HTTP response status and headers in the output',\n }),\n input: Flags.string({\n description:\n 'Read the raw request body from a file (use \"-\" for stdin). Sent without a default Content-Type - provide one with -H when the API requires it',\n exclusive: ['field', 'raw-field'],\n helpValue: '<file>',\n }),\n method: Flags.string({\n char: 'X',\n description: 'HTTP method to use (default GET, or POST when fields or --input are provided)',\n helpValue: '<method>',\n }),\n pretty: Flags.boolean({\n default: false,\n description: 'Colorize JSON output',\n }),\n 'project-hosted': Flags.boolean({\n description: 'Force the request to the project API host (<projectId>.api.sanity.io)',\n exclusive: ['global'],\n }),\n 'raw-field': Flags.string({\n char: 'f',\n description: 'Add a string parameter (key=value)',\n helpValue: '<key=value>',\n multiple: true,\n }),\n token: Flags.string({\n char: 't',\n description: 'API token to authenticate with, instead of the logged-in user token',\n exclusive: ['anonymous'],\n helpValue: '<token>',\n }),\n }\n\n /**\n * Read the raw body for `--input`, or the content referenced by a `@-`\n * field value. Returns raw bytes so binary bodies survive unmodified.\n * Extracted so tests can substitute stdin.\n */\n protected readStdin(): Promise<Buffer> {\n return buffer(process.stdin)\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ApiCommand)\n\n try {\n await this.performRequest(args.endpoint, flags)\n } catch (error) {\n if (error instanceof ApiUsageError) {\n this.error(error.message, {exit: exitCodes.USAGE_ERROR})\n }\n throw error\n }\n }\n\n private async performRequest(\n endpoint: string,\n flags: (typeof ApiCommand.prototype)['flags'],\n ): Promise<void> {\n const stdin = await this.resolveStdin(flags)\n\n const fields = parseFields({\n fields: flags.field,\n rawFields: flags['raw-field'],\n readFile: (path) => readFileSync(path, 'utf8'),\n stdin: stdin?.toString('utf8'),\n })\n const hasFields = Object.keys(fields).length > 0\n\n const method = this.resolveMethod(flags.method, hasFields || flags.input !== undefined)\n const useQueryFields = BODYLESS_METHODS.has(method)\n\n const body = await this.resolveBody(flags, fields, hasFields, useQueryFields, stdin)\n const query = useQueryFields && hasFields ? fieldsToQuery(fields) : {}\n\n const resolved = await this.resolveTarget(endpoint, flags)\n apiDebug('Resolved endpoint', resolved)\n\n const response = await performApiRequest({\n ...(body === undefined ? {} : {body}),\n headers: parseHeaders(flags.header ?? []),\n method,\n query,\n resolved,\n token: flags.token,\n unauthenticated: flags.anonymous,\n })\n\n this.printResponse(response, flags)\n\n if (response.statusCode >= 400) {\n const statusLine = `HTTP ${response.statusCode}${\n response.statusMessage ? ` ${response.statusMessage}` : ''\n }`\n const loginHint =\n response.statusCode === 401 && !flags.anonymous\n ? `. You may need to login again with ${styleText('cyan', 'sanity login')}`\n : ''\n this.error(`${statusLine}${loginHint}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private printResponse(response: ApiResponse, flags: {include: boolean; pretty: boolean}): void {\n if (flags.include) {\n this.log(\n `HTTP ${response.statusCode}${response.statusMessage ? ` ${response.statusMessage}` : ''}`,\n )\n for (const [key, value] of Object.entries(response.headers)) {\n this.log(`${key}: ${value}`)\n }\n this.log('')\n }\n\n if (!response.jsonBody) {\n if (response.rawBody !== '') this.log(response.rawBody)\n return\n }\n\n this.log(flags.pretty ? colorizeJson(response.body) : JSON.stringify(response.body, null, 2))\n }\n\n private async resolveBody(\n flags: {input?: string},\n fields: Record<string, FieldValue>,\n hasFields: boolean,\n useQueryFields: boolean,\n stdin: Buffer | undefined,\n ): Promise<Buffer | unknown | undefined> {\n if (flags.input !== undefined) {\n if (flags.input === '-') return stdin\n try {\n // Read raw bytes - `--input` bodies may be binary (eg asset uploads)\n return await readFile(flags.input)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new ApiUsageError(`Failed to read --input file \"${flags.input}\": ${message}`)\n }\n }\n\n return hasFields && !useQueryFields ? fields : undefined\n }\n\n private resolveMethod(methodFlag: string | undefined, hasBody: boolean): string {\n if (methodFlag === undefined) {\n return hasBody ? 'POST' : 'GET'\n }\n if (!METHOD_RE.test(methodFlag)) {\n throw new ApiUsageError(`Invalid HTTP method \"${methodFlag}\"`)\n }\n return methodFlag.toUpperCase()\n }\n\n private async resolveStdin(flags: {\n field?: string[]\n input?: string\n }): Promise<Buffer | undefined> {\n const needsStdin =\n flags.input === '-' || (flags.field ?? []).some((field) => field.endsWith('=@-'))\n return needsStdin ? this.readStdin() : undefined\n }\n\n private async resolveTarget(\n endpoint: string,\n flags: {\n 'api-version'?: string\n dataset?: string\n global?: boolean\n 'project-hosted'?: boolean\n 'project-id'?: string\n },\n ): Promise<ResolvedEndpoint> {\n const cliConfig = await this.tryGetCliConfig()\n\n const baseOptions = {\n apiVersion: flags['api-version'],\n dataset: flags.dataset ?? cliConfig.api?.dataset,\n endpoint,\n forceHost: flags.global ? ('global' as const) : undefined,\n routes: apiRoutes,\n }\n const forceHost = flags['project-hosted'] ? ('project' as const) : baseOptions.forceHost\n\n try {\n return resolveEndpoint({\n ...baseOptions,\n forceHost,\n projectId: flags['project-id'] ?? cliConfig.api?.projectId,\n })\n } catch (error) {\n if (!(error instanceof ProjectIdRequiredError)) throw error\n\n // The request needs a project ID that flags/config didn't provide -\n // resolve one interactively (or fail with actionable suggestions).\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n return resolveEndpoint({...baseOptions, forceHost, projectId})\n }\n }\n}\n\nfunction parseHeaders(headerFlags: string[]): Record<string, string> {\n // Null-prototype for the same reason as the field containers in parseFields\n const headers: Record<string, string> = Object.create(null)\n\n for (const header of headerFlags) {\n const separatorIndex = header.indexOf(':')\n if (separatorIndex < 1) {\n throw new ApiUsageError(`Invalid --header \"${header}\": expected \"key: value\" format`)\n }\n headers[header.slice(0, separatorIndex).trim()] = header.slice(separatorIndex + 1).trim()\n }\n\n return headers\n}\n"],"names":["readFileSync","readFile","buffer","styleText","Args","Flags","colorizeJson","exitCodes","SanityCommand","subdebug","ApiUsageError","ProjectIdRequiredError","fieldsToQuery","parseFields","resolveEndpoint","apiRoutes","promptForProject","performApiRequest","getDatasetFlag","getProjectIdFlag","apiDebug","METHOD_RE","BODYLESS_METHODS","Set","ApiCommand","args","endpoint","string","description","required","examples","command","flags","semantics","anonymous","boolean","default","helpValue","field","char","multiple","global","exclusive","header","include","input","method","pretty","token","readStdin","process","stdin","run","parse","performRequest","error","message","exit","USAGE_ERROR","resolveStdin","fields","rawFields","path","toString","hasFields","Object","keys","length","resolveMethod","undefined","useQueryFields","has","body","resolveBody","query","resolved","resolveTarget","response","headers","parseHeaders","unauthenticated","printResponse","statusCode","statusLine","statusMessage","loginHint","RUNTIME_ERROR","log","key","value","entries","jsonBody","rawBody","JSON","stringify","Error","String","methodFlag","hasBody","test","toUpperCase","needsStdin","some","endsWith","cliConfig","tryGetCliConfig","baseOptions","apiVersion","dataset","api","forceHost","routes","projectId","getProjectId","fallback","headerFlags","create","separatorIndex","indexOf","slice","trim"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/commands/api.ts"],"sourcesContent":["import {readFileSync} from 'node:fs'\nimport {readFile} from 'node:fs/promises'\nimport {buffer} from 'node:stream/consumers'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {colorizeJson, exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {ApiUsageError, ProjectIdRequiredError} from '../actions/api/errors.js'\nimport {fieldsToQuery, type FieldValue, parseFields} from '../actions/api/parseFields.js'\nimport {type ResolvedEndpoint, resolveEndpoint} from '../actions/api/resolveEndpoint.js'\nimport {apiRoutes} from '../generated/apiRoutes.js'\nimport {promptForProject} from '../prompts/promptForProject.js'\nimport {type ApiResponse, performApiRequest} from '../services/api.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../util/sharedFlags.js'\n\nconst apiDebug = subdebug('api')\n\nconst METHOD_RE = /^[a-z]+$/i\n\nconst BODYLESS_METHODS = new Set(['GET', 'HEAD'])\n\nexport class ApiCommand extends SanityCommand<typeof ApiCommand> {\n static override args = {\n endpoint: Args.string({\n description:\n 'API path (eg \"projects\" or \"data/query/{dataset}\"), optionally with placeholders, or a full https://*.api.sanity.io URL',\n required: true,\n }),\n }\n\n static override description = `Make an authenticated HTTP request to a Sanity API\n\nThe endpoint argument is an API path as documented in the published OpenAPI\nspecifications - list them with \"sanity openapi list\" and inspect one with\n\"sanity openapi get <slug>\". Paths can be copied verbatim from the specs:\n{projectId} and {dataset} placeholders are filled in from flags or the CLI\nconfiguration, and the API host (api.sanity.io or <projectId>.api.sanity.io)\nis chosen based on the specs' routing information.\n\nThe default request method is GET, or POST when fields or --input are\nprovided. For GET/HEAD requests, fields are sent as query parameters;\notherwise they are combined into a JSON request body sent with\n\"Content-Type: application/json\". Raw --input bodies are sent without a\ndefault Content-Type - provide one with -H when the API requires it. The\nresponse body is written to stdout.\n\nRequests are authenticated with the token from \"sanity login\". To use a\nspecific token instead - for example in CI or when the CLI is not logged in\n- pass --token or set the SANITY_AUTH_TOKEN environment variable. Pass\n--anonymous to send no token at all.`\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> users/me',\n description: 'Get the current user',\n },\n {\n command: '<%= config.bin %> <%= command.id %> projects/{projectId}',\n description: 'Get the current project (placeholder filled from CLI config)',\n },\n {\n command: `<%= config.bin %> <%= command.id %> 'data/query/{dataset}' -f query='*[_type == \"movie\"][0..2]'`,\n description: 'Run a GROQ query against the project host',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> projects/{projectId} -X PATCH -F displayName=\"My project\"',\n description: 'Send a JSON body built from typed fields',\n },\n {\n command: `echo '{\"mutations\": []}' | <%= config.bin %> <%= command.id %> 'data/mutate/{dataset}' --input - -H 'Content-Type: application/json'`,\n description: 'Send a raw request body from stdin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> jobs/123 --include --api-version v2025-02-19',\n description: 'Include the response status and headers, pinning the API version',\n },\n {\n command: 'SANITY_AUTH_TOKEN=<token> <%= config.bin %> <%= command.id %> users/me',\n description: 'Authenticate with a specific token instead of the logged-in session',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID for {projectId} placeholders and project-hosted APIs',\n semantics: 'override',\n }),\n ...getDatasetFlag({\n description: 'Dataset for {dataset} placeholders',\n semantics: 'override',\n }),\n anonymous: Flags.boolean({\n default: false,\n description: 'Send the request without an authorization token',\n }),\n 'api-version': Flags.string({\n description:\n 'API version to use (eg v2025-02-19). Defaults to a version embedded in the endpoint path, or the version from the matching OpenAPI spec',\n helpValue: '<version>',\n }),\n field: Flags.string({\n char: 'F',\n description:\n 'Add a typed parameter (key=value): true/false/null and numbers are converted, @file reads the value from a file, @- from stdin',\n helpValue: '<key=value>',\n multiple: true,\n }),\n global: Flags.boolean({\n description: 'Force the request to the global API host (api.sanity.io)',\n exclusive: ['project-hosted'],\n }),\n header: Flags.string({\n char: 'H',\n description: 'Add an HTTP request header (key: value)',\n helpValue: '<key:value>',\n multiple: true,\n }),\n include: Flags.boolean({\n char: 'i',\n default: false,\n description: 'Include the HTTP response status and headers in the output',\n }),\n input: Flags.string({\n description:\n 'Read the raw request body from a file (use \"-\" for stdin). Sent without a default Content-Type - provide one with -H when the API requires it',\n exclusive: ['field', 'raw-field'],\n helpValue: '<file>',\n }),\n method: Flags.string({\n char: 'X',\n description: 'HTTP method to use (default GET, or POST when fields or --input are provided)',\n helpValue: '<method>',\n }),\n pretty: Flags.boolean({\n default: false,\n description: 'Colorize JSON output',\n }),\n 'project-hosted': Flags.boolean({\n description: 'Force the request to the project API host (<projectId>.api.sanity.io)',\n exclusive: ['global'],\n }),\n 'raw-field': Flags.string({\n char: 'f',\n description: 'Add a string parameter (key=value)',\n helpValue: '<key=value>',\n multiple: true,\n }),\n token: Flags.string({\n char: 't',\n description: 'API token to authenticate with, instead of the logged-in user token',\n exclusive: ['anonymous'],\n helpValue: '<token>',\n }),\n }\n\n /**\n * Read the raw body for `--input`, or the content referenced by a `@-`\n * field value. Returns raw bytes so binary bodies survive unmodified.\n * Extracted so tests can substitute stdin.\n */\n protected readStdin(): Promise<Buffer> {\n return buffer(process.stdin)\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ApiCommand)\n\n try {\n await this.performRequest(args.endpoint, flags)\n } catch (error) {\n if (error instanceof ApiUsageError) {\n this.error(error.message, {exit: exitCodes.USAGE_ERROR})\n }\n throw error\n }\n }\n\n private async performRequest(\n endpoint: string,\n flags: (typeof ApiCommand.prototype)['flags'],\n ): Promise<void> {\n const stdin = await this.resolveStdin(flags)\n\n const fields = parseFields({\n fields: flags.field,\n rawFields: flags['raw-field'],\n readFile: (path) => readFileSync(path, 'utf8'),\n stdin: stdin?.toString('utf8'),\n })\n const hasFields = Object.keys(fields).length > 0\n\n const method = this.resolveMethod(flags.method, hasFields || flags.input !== undefined)\n const useQueryFields = BODYLESS_METHODS.has(method)\n\n const body = await this.resolveBody(flags, fields, hasFields, useQueryFields, stdin)\n const query = useQueryFields && hasFields ? fieldsToQuery(fields) : {}\n\n const resolved = await this.resolveTarget(endpoint, flags)\n apiDebug('Resolved endpoint', resolved)\n\n const response = await performApiRequest({\n ...(body === undefined ? {} : {body}),\n headers: parseHeaders(flags.header ?? []),\n method,\n query,\n resolved,\n token: flags.token,\n unauthenticated: flags.anonymous,\n })\n\n this.printResponse(response, flags)\n\n if (response.statusCode >= 400) {\n const statusLine = `HTTP ${response.statusCode}${\n response.statusMessage ? ` ${response.statusMessage}` : ''\n }`\n const loginHint =\n response.statusCode === 401 && !flags.anonymous\n ? `. You may need to login again with ${styleText('cyan', 'sanity login')}`\n : ''\n this.error(`${statusLine}${loginHint}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private printResponse(response: ApiResponse, flags: {include: boolean; pretty: boolean}): void {\n if (flags.include) {\n this.log(\n `HTTP ${response.statusCode}${response.statusMessage ? ` ${response.statusMessage}` : ''}`,\n )\n for (const [key, value] of Object.entries(response.headers)) {\n this.log(`${key}: ${value}`)\n }\n this.log('')\n }\n\n if (!response.jsonBody) {\n if (response.rawBody !== '') this.log(response.rawBody)\n return\n }\n\n this.log(flags.pretty ? colorizeJson(response.body) : JSON.stringify(response.body, null, 2))\n }\n\n private async resolveBody(\n flags: {input?: string},\n fields: Record<string, FieldValue>,\n hasFields: boolean,\n useQueryFields: boolean,\n stdin: Buffer | undefined,\n ): Promise<Buffer | unknown | undefined> {\n if (flags.input !== undefined) {\n if (flags.input === '-') return stdin\n try {\n // Read raw bytes - `--input` bodies may be binary (eg asset uploads)\n return await readFile(flags.input)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new ApiUsageError(`Failed to read --input file \"${flags.input}\": ${message}`)\n }\n }\n\n return hasFields && !useQueryFields ? fields : undefined\n }\n\n private resolveMethod(methodFlag: string | undefined, hasBody: boolean): string {\n if (methodFlag === undefined) {\n return hasBody ? 'POST' : 'GET'\n }\n if (!METHOD_RE.test(methodFlag)) {\n throw new ApiUsageError(`Invalid HTTP method \"${methodFlag}\"`)\n }\n return methodFlag.toUpperCase()\n }\n\n private async resolveStdin(flags: {\n field?: string[]\n input?: string\n }): Promise<Buffer | undefined> {\n const needsStdin =\n flags.input === '-' || (flags.field ?? []).some((field) => field.endsWith('=@-'))\n return needsStdin ? this.readStdin() : undefined\n }\n\n private async resolveTarget(\n endpoint: string,\n flags: {\n 'api-version'?: string\n dataset?: string\n global?: boolean\n project?: string\n 'project-hosted'?: boolean\n },\n ): Promise<ResolvedEndpoint> {\n const cliConfig = await this.tryGetCliConfig()\n\n const baseOptions = {\n apiVersion: flags['api-version'],\n dataset: flags.dataset ?? cliConfig.api?.dataset,\n endpoint,\n forceHost: flags.global ? ('global' as const) : undefined,\n routes: apiRoutes,\n }\n const forceHost = flags['project-hosted'] ? ('project' as const) : baseOptions.forceHost\n\n try {\n return resolveEndpoint({\n ...baseOptions,\n forceHost,\n projectId: flags.project ?? cliConfig.api?.projectId,\n })\n } catch (error) {\n if (!(error instanceof ProjectIdRequiredError)) throw error\n\n // The request needs a project ID that flags/config didn't provide -\n // resolve one interactively (or fail with actionable suggestions).\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n return resolveEndpoint({...baseOptions, forceHost, projectId})\n }\n }\n}\n\nfunction parseHeaders(headerFlags: string[]): Record<string, string> {\n // Null-prototype for the same reason as the field containers in parseFields\n const headers: Record<string, string> = Object.create(null)\n\n for (const header of headerFlags) {\n const separatorIndex = header.indexOf(':')\n if (separatorIndex < 1) {\n throw new ApiUsageError(`Invalid --header \"${header}\": expected \"key: value\" format`)\n }\n headers[header.slice(0, separatorIndex).trim()] = header.slice(separatorIndex + 1).trim()\n }\n\n return headers\n}\n"],"names":["readFileSync","readFile","buffer","styleText","Args","Flags","colorizeJson","exitCodes","SanityCommand","subdebug","ApiUsageError","ProjectIdRequiredError","fieldsToQuery","parseFields","resolveEndpoint","apiRoutes","promptForProject","performApiRequest","getDatasetFlag","getProjectIdFlag","apiDebug","METHOD_RE","BODYLESS_METHODS","Set","ApiCommand","args","endpoint","string","description","required","examples","command","flags","semantics","anonymous","boolean","default","helpValue","field","char","multiple","global","exclusive","header","include","input","method","pretty","token","readStdin","process","stdin","run","parse","performRequest","error","message","exit","USAGE_ERROR","resolveStdin","fields","rawFields","path","toString","hasFields","Object","keys","length","resolveMethod","undefined","useQueryFields","has","body","resolveBody","query","resolved","resolveTarget","response","headers","parseHeaders","unauthenticated","printResponse","statusCode","statusLine","statusMessage","loginHint","RUNTIME_ERROR","log","key","value","entries","jsonBody","rawBody","JSON","stringify","Error","String","methodFlag","hasBody","test","toUpperCase","needsStdin","some","endsWith","cliConfig","tryGetCliConfig","baseOptions","apiVersion","dataset","api","forceHost","routes","projectId","project","getProjectId","fallback","headerFlags","create","separatorIndex","indexOf","slice","trim"],"mappings":"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"}
|
|
@@ -60,7 +60,7 @@ export class UploadAssetCommand extends SanityCommand {
|
|
|
60
60
|
description: 'Upload an image using the configured project'
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
|
-
command: '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project
|
|
63
|
+
command: '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project abc123 --dataset production',
|
|
64
64
|
description: 'Upload a file with explicit project, dataset, and MIME type'
|
|
65
65
|
},
|
|
66
66
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/assets/upload.ts"],"sourcesContent":["import {basename, resolve} from 'node:path'\n\nimport {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {AssetFileError} from '../../actions/assets/assetFileError.js'\nimport {ingestAssetFromUrlWithProgress} from '../../actions/assets/ingestAssetFromUrlWithProgress.js'\nimport {uploadAssetWithProgress} from '../../actions/assets/uploadAssetWithProgress.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type AssetType} from '../../services/assets.js'\nimport {getAssetFilenameError, getIngestUrlError} from '../../util/assetSourceValidation.js'\nimport {getAssetUploadErrorMessage} from '../../util/assetUploadErrors.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\nimport {defineCommandTelemetry} from '../../util/telemetry/commandTelemetry.js'\n\nconst uploadAssetDebug = subdebug('assets:upload')\n\nconst flags = {\n ...getProjectIdFlag({\n description: 'Project ID to upload the asset to',\n semantics: 'override',\n }),\n ...getDatasetFlag({description: 'Dataset to upload the asset to', semantics: 'override'}),\n 'content-type': Flags.string({\n description:\n 'MIME type of the asset, such as image/png or application/pdf. Only applies to --file',\n helpValue: '<mime-type>',\n }),\n file: Flags.string({\n description: 'Path to the local file to upload',\n exactlyOne: ['file', 'from-url'],\n helpValue: '<path>',\n }),\n filename: Flags.string({\n description:\n 'Original filename stored on the asset document. Defaults to the local filename when using --file',\n helpValue: '<filename>',\n }),\n 'from-url': Flags.string({\n description:\n 'https URL for Sanity to fetch the asset from, instead of uploading a local file. Must be reachable from the public internet without authentication',\n exactlyOne: ['file', 'from-url'],\n helpValue: '<url>',\n }),\n type: Flags.custom<AssetType>({\n default: 'image',\n description: 'Asset type to create',\n options: ['image', 'file'],\n })(),\n} satisfies FlagInput\n\nexport class UploadAssetCommand extends SanityCommand<typeof UploadAssetCommand> {\n static override description =\n 'Upload one image or file to a Sanity dataset, from a local path or a URL, and print the asset document as JSON'\n\n static override examples = [\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./hero.png --type image --dataset production',\n description: 'Upload an image using the configured project',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project-id abc123 --dataset production',\n description: 'Upload a file with explicit project, dataset, and MIME type',\n },\n {\n command:\n \"<%= config.bin %> <%= command.id %> --from-url 'https://example.com/hero.png' --type image --dataset production\",\n description: 'Have Sanity fetch an image from a public URL',\n },\n ]\n\n static override flags = flags\n\n static override hiddenAliases: string[] = ['asset:upload']\n\n static telemetry = defineCommandTelemetry(flags, {\n redact: ['file', 'filename', 'from-url'],\n })\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(UploadAssetCommand)\n const sourceUrl = flags['from-url']\n\n if (sourceUrl && flags['content-type']) {\n this.error(\n 'Asset upload failed: --content-type cannot be combined with --from-url. Sanity derives the MIME type automatically.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const sourceUrlError = sourceUrl === undefined ? undefined : getIngestUrlError(sourceUrl)\n if (sourceUrlError) {\n this.error(`Asset upload failed: ${sourceUrlError}`, {exit: exitCodes.USAGE_ERROR})\n }\n\n const filenameError =\n flags.filename === undefined ? undefined : getAssetFilenameError(flags.filename)\n if (filenameError) {\n this.error(`Asset upload failed: ${filenameError}`, {exit: exitCodes.USAGE_ERROR})\n }\n\n const cliConfig = await this.tryGetCliConfig()\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n const dataset = flags.dataset ?? cliConfig.api?.dataset\n if (!dataset) {\n this.error(\n 'Asset upload failed: Dataset is required. Pass --dataset <name> or configure a dataset in sanity.cli.ts.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const isInteractive = this.resolveIsInteractive()\n\n try {\n let asset\n if (sourceUrl === undefined) {\n // `exactlyOne` on the flags guarantees a source, but not to the compiler.\n if (flags.file === undefined) {\n this.error('Asset upload failed: Pass either --file <path> or --from-url <url>.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const filePath = resolve(flags.file)\n asset = await uploadAssetWithProgress({\n assetType: flags.type,\n contentType: flags['content-type'],\n dataset,\n filename: flags.filename ?? basename(filePath),\n filePath,\n isInteractive,\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n })\n } else {\n asset = await ingestAssetFromUrlWithProgress({\n assetType: flags.type,\n dataset,\n filename: flags.filename,\n isInteractive,\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n url: sourceUrl,\n })\n }\n const fieldType = flags.type === 'image' ? 'image' : 'file'\n\n this.log(\n JSON.stringify(\n {\n asset: {\n _id: asset._id,\n _type: asset._type,\n extension: asset.extension,\n mimeType: asset.mimeType,\n originalFilename: asset.originalFilename,\n size: asset.size,\n url: asset.url,\n },\n reference: {\n _type: fieldType,\n asset: {_ref: asset._id, _type: 'reference'},\n },\n },\n null,\n 2,\n ),\n )\n } catch (error) {\n if (error instanceof Error && error.message === 'SIGINT') throw error\n if (error instanceof AssetFileError) {\n if (error.reason === 'not-file') {\n this.error('Asset upload failed: --file must point to a file, not a directory.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n this.error(\n 'Asset upload failed: Cannot read the local file. Check that --file points to a readable file, then retry.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n uploadAssetDebug('Asset upload failed', error)\n this.error(\n getAssetUploadErrorMessage(error, {\n fromUrl: sourceUrl !== undefined,\n target: 'dataset',\n }),\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n}\n"],"names":["basename","resolve","Flags","exitCodes","SanityCommand","subdebug","AssetFileError","ingestAssetFromUrlWithProgress","uploadAssetWithProgress","promptForProject","getAssetFilenameError","getIngestUrlError","getAssetUploadErrorMessage","getDatasetFlag","getProjectIdFlag","defineCommandTelemetry","uploadAssetDebug","flags","description","semantics","string","helpValue","file","exactlyOne","filename","type","custom","default","options","UploadAssetCommand","examples","command","hiddenAliases","telemetry","redact","run","parse","sourceUrl","error","exit","USAGE_ERROR","sourceUrlError","undefined","filenameError","cliConfig","tryGetCliConfig","projectId","getProjectId","fallback","dataset","api","isInteractive","resolveIsInteractive","asset","filePath","assetType","contentType","logToStderr","message","url","fieldType","log","JSON","stringify","_id","_type","extension","mimeType","originalFilename","size","reference","_ref","Error","reason","fromUrl","target","RUNTIME_ERROR"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/assets/upload.ts"],"sourcesContent":["import {basename, resolve} from 'node:path'\n\nimport {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {AssetFileError} from '../../actions/assets/assetFileError.js'\nimport {ingestAssetFromUrlWithProgress} from '../../actions/assets/ingestAssetFromUrlWithProgress.js'\nimport {uploadAssetWithProgress} from '../../actions/assets/uploadAssetWithProgress.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type AssetType} from '../../services/assets.js'\nimport {getAssetFilenameError, getIngestUrlError} from '../../util/assetSourceValidation.js'\nimport {getAssetUploadErrorMessage} from '../../util/assetUploadErrors.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\nimport {defineCommandTelemetry} from '../../util/telemetry/commandTelemetry.js'\n\nconst uploadAssetDebug = subdebug('assets:upload')\n\nconst flags = {\n ...getProjectIdFlag({\n description: 'Project ID to upload the asset to',\n semantics: 'override',\n }),\n ...getDatasetFlag({description: 'Dataset to upload the asset to', semantics: 'override'}),\n 'content-type': Flags.string({\n description:\n 'MIME type of the asset, such as image/png or application/pdf. Only applies to --file',\n helpValue: '<mime-type>',\n }),\n file: Flags.string({\n description: 'Path to the local file to upload',\n exactlyOne: ['file', 'from-url'],\n helpValue: '<path>',\n }),\n filename: Flags.string({\n description:\n 'Original filename stored on the asset document. Defaults to the local filename when using --file',\n helpValue: '<filename>',\n }),\n 'from-url': Flags.string({\n description:\n 'https URL for Sanity to fetch the asset from, instead of uploading a local file. Must be reachable from the public internet without authentication',\n exactlyOne: ['file', 'from-url'],\n helpValue: '<url>',\n }),\n type: Flags.custom<AssetType>({\n default: 'image',\n description: 'Asset type to create',\n options: ['image', 'file'],\n })(),\n} satisfies FlagInput\n\nexport class UploadAssetCommand extends SanityCommand<typeof UploadAssetCommand> {\n static override description =\n 'Upload one image or file to a Sanity dataset, from a local path or a URL, and print the asset document as JSON'\n\n static override examples = [\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./hero.png --type image --dataset production',\n description: 'Upload an image using the configured project',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project abc123 --dataset production',\n description: 'Upload a file with explicit project, dataset, and MIME type',\n },\n {\n command:\n \"<%= config.bin %> <%= command.id %> --from-url 'https://example.com/hero.png' --type image --dataset production\",\n description: 'Have Sanity fetch an image from a public URL',\n },\n ]\n\n static override flags = flags\n\n static override hiddenAliases: string[] = ['asset:upload']\n\n static telemetry = defineCommandTelemetry(flags, {\n redact: ['file', 'filename', 'from-url'],\n })\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(UploadAssetCommand)\n const sourceUrl = flags['from-url']\n\n if (sourceUrl && flags['content-type']) {\n this.error(\n 'Asset upload failed: --content-type cannot be combined with --from-url. Sanity derives the MIME type automatically.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const sourceUrlError = sourceUrl === undefined ? undefined : getIngestUrlError(sourceUrl)\n if (sourceUrlError) {\n this.error(`Asset upload failed: ${sourceUrlError}`, {exit: exitCodes.USAGE_ERROR})\n }\n\n const filenameError =\n flags.filename === undefined ? undefined : getAssetFilenameError(flags.filename)\n if (filenameError) {\n this.error(`Asset upload failed: ${filenameError}`, {exit: exitCodes.USAGE_ERROR})\n }\n\n const cliConfig = await this.tryGetCliConfig()\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n const dataset = flags.dataset ?? cliConfig.api?.dataset\n if (!dataset) {\n this.error(\n 'Asset upload failed: Dataset is required. Pass --dataset <name> or configure a dataset in sanity.cli.ts.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const isInteractive = this.resolveIsInteractive()\n\n try {\n let asset\n if (sourceUrl === undefined) {\n // `exactlyOne` on the flags guarantees a source, but not to the compiler.\n if (flags.file === undefined) {\n this.error('Asset upload failed: Pass either --file <path> or --from-url <url>.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const filePath = resolve(flags.file)\n asset = await uploadAssetWithProgress({\n assetType: flags.type,\n contentType: flags['content-type'],\n dataset,\n filename: flags.filename ?? basename(filePath),\n filePath,\n isInteractive,\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n })\n } else {\n asset = await ingestAssetFromUrlWithProgress({\n assetType: flags.type,\n dataset,\n filename: flags.filename,\n isInteractive,\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n url: sourceUrl,\n })\n }\n const fieldType = flags.type === 'image' ? 'image' : 'file'\n\n this.log(\n JSON.stringify(\n {\n asset: {\n _id: asset._id,\n _type: asset._type,\n extension: asset.extension,\n mimeType: asset.mimeType,\n originalFilename: asset.originalFilename,\n size: asset.size,\n url: asset.url,\n },\n reference: {\n _type: fieldType,\n asset: {_ref: asset._id, _type: 'reference'},\n },\n },\n null,\n 2,\n ),\n )\n } catch (error) {\n if (error instanceof Error && error.message === 'SIGINT') throw error\n if (error instanceof AssetFileError) {\n if (error.reason === 'not-file') {\n this.error('Asset upload failed: --file must point to a file, not a directory.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n this.error(\n 'Asset upload failed: Cannot read the local file. Check that --file points to a readable file, then retry.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n uploadAssetDebug('Asset upload failed', error)\n this.error(\n getAssetUploadErrorMessage(error, {\n fromUrl: sourceUrl !== undefined,\n target: 'dataset',\n }),\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n}\n"],"names":["basename","resolve","Flags","exitCodes","SanityCommand","subdebug","AssetFileError","ingestAssetFromUrlWithProgress","uploadAssetWithProgress","promptForProject","getAssetFilenameError","getIngestUrlError","getAssetUploadErrorMessage","getDatasetFlag","getProjectIdFlag","defineCommandTelemetry","uploadAssetDebug","flags","description","semantics","string","helpValue","file","exactlyOne","filename","type","custom","default","options","UploadAssetCommand","examples","command","hiddenAliases","telemetry","redact","run","parse","sourceUrl","error","exit","USAGE_ERROR","sourceUrlError","undefined","filenameError","cliConfig","tryGetCliConfig","projectId","getProjectId","fallback","dataset","api","isInteractive","resolveIsInteractive","asset","filePath","assetType","contentType","logToStderr","message","url","fieldType","log","JSON","stringify","_id","_type","extension","mimeType","originalFilename","size","reference","_ref","Error","reason","fromUrl","target","RUNTIME_ERROR"],"mappings":"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"}
|
package/dist/commands/codemod.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/commands/codemod.ts"],"sourcesContent":["import {execSync, spawn} from 'node:child_process'\nimport {existsSync} from 'node:fs'\nimport path from 'node:path'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {codemods} from '../actions/codemods/index.js'\nimport {type CodeMod} from '../actions/codemods/types.js'\n\nconst codemodeDebug = subdebug('codemod')\n\nexport class CodemodCommand extends SanityCommand<typeof CodemodCommand> {\n static override args = {\n codemodName: Args.string({\n description: 'Name of the codemod to run',\n required: false,\n }),\n }\n\n static override description = 'Updates Sanity Studio codebase with a code modification script'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Show available code mods',\n },\n {\n command: '<%= config.bin %> <%= command.id %> reactIconsV3 --dry',\n description: 'Run codemod to transform react-icons imports (dry run)',\n },\n ]\n\n static override flags = {\n dry: Flags.boolean({\n default: false,\n description: 'Dry run (no changes are made to files)',\n }),\n extensions: Flags.string({\n default: 'js,ts,tsx',\n description: 'Transform files with these file extensions (comma separated)',\n }),\n 'no-verify': Flags.boolean({\n default: false,\n description: 'Skip verification steps before running codemod',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CodemodCommand)\n const {codemodName} = args\n const workDir = process.cwd()\n\n if (!codemodName) {\n this.printMods()\n return\n }\n\n // Make a record where all the keys of the available codemods are lowercased,\n // eg `reactIconsV3` turns into `{reacticonsv3: CodeMod}`\n const normalizedMods: Map<string, CodeMod> = new Map()\n for (const [originalName, mod] of Object.entries(codemods)) {\n normalizedMods.set(originalName.toLowerCase(), mod)\n }\n\n // Verify that mod exists\n const mod = normalizedMods.get(codemodName.toLowerCase())\n if (!mod) {\n this.error(`Codemod with name \"${codemodName}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n // Verify if there is any verification defined, and user has not opted out of it\n if (typeof mod.verify === 'function' && !flags['no-verify']) {\n try {\n await mod.verify({workDir})\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n codemodeDebug('Verification failed: %s', err.message)\n this.error(`Verification failed: ${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n // If extensions are defined, they come in as CSV, so split and normalize them\n const exts = flags.extensions\n ? flags.extensions.split(',').map((ext) => ext.trim().replace(/^\\./, ''))\n : ['js', 'ts', 'tsx']\n\n const dryRun = flags.dry\n\n // We use npx to run jscodeshift, so verify that it exists and works before using it\n this.ensureNpx()\n\n const prepared = await mod.prepare?.({output: this.output, workDir})\n // jscodeshift JSON-parses option values, so encode them to keep strings like \"2024\" as strings\n const transformOptions = Object.entries(prepared?.options ?? {}).map(\n ([key, value]) => `--${key}=${JSON.stringify(value)}`,\n )\n\n // If there is a gitignore in the target folder, we want to use that to know\n // which folders to ignore - eg `dist`, `coverage` or whatever\n const hasGitIgnore = existsSync(path.join(workDir, '.gitignore'))\n\n // Build the CLI command arguments\n const cliRoot = path.resolve(import.meta.dirname, '../..')\n const modPath = path.resolve(path.join(cliRoot, 'codemods', mod.filename))\n const cmdArgs = [\n 'jscodeshift',\n '--ignore-pattern',\n 'node_modules',\n '--ignore-pattern',\n 'dist',\n hasGitIgnore && '--ignore-config',\n hasGitIgnore && '.gitignore',\n '-t',\n modPath,\n '--extensions',\n exts.join(','),\n dryRun && '--dry',\n ...transformOptions,\n ...(prepared?.paths ?? [workDir]),\n ].filter((item): item is string => typeof item === 'string')\n\n const child = spawn('npx', cmdArgs, {\n stdio: 'inherit',\n })\n\n const handleSigint = () => {\n child.kill(2)\n }\n\n process.on('SIGINT', handleSigint)\n\n // Wait inside run() so oclif handles the exit, rather than throwing from an event callback\n const code = await new Promise<number | null>((resolve) => {\n child.on('close', resolve)\n })\n process.removeListener('SIGINT', handleSigint)\n this.exit(code || undefined)\n }\n\n private ensureNpx(): void {\n try {\n const npxHelp = execSync('npx --help', {encoding: 'utf8'})\n if (!npxHelp.includes('npm')) {\n this.error('Not the npx we expected', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch {\n this.error(\n `Failed to run \"npx\" - required to run codemods. Do you have a recent version of npm installed?`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n\n private printMods(): void {\n this.log('Available code modifications:\\n')\n\n for (const [modName, mod] of Object.entries(codemods)) {\n this.log(`${modName} - ${mod.purpose}`)\n }\n }\n}\n"],"names":["execSync","spawn","existsSync","path","Args","Flags","exitCodes","SanityCommand","subdebug","codemods","codemodeDebug","CodemodCommand","args","codemodName","string","description","required","examples","command","flags","dry","boolean","default","extensions","run","parse","workDir","process","cwd","printMods","normalizedMods","Map","originalName","mod","Object","entries","set","toLowerCase","get","error","exit","RUNTIME_ERROR","verify","err","Error","String","message","exts","split","map","ext","trim","replace","dryRun","ensureNpx","prepared","prepare","output","transformOptions","options","key","value","JSON","stringify","hasGitIgnore","join","cliRoot","resolve","dirname","modPath","filename","cmdArgs","paths","filter","item","child","stdio","handleSigint","kill","on","code","Promise","removeListener","undefined","npxHelp","encoding","includes","log","modName","purpose"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,KAAK,QAAO,qBAAoB;AAClD,SAAQC,UAAU,QAAO,UAAS;AAClC,OAAOC,UAAU,YAAW;AAE5B,SAAQC,IAAI,EAAEC,KAAK,QAAO,cAAa;AACvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AAEnE,SAAQC,QAAQ,QAAO,+BAA8B;AAGrD,MAAMC,gBAAgBF,SAAS;AAE/B,OAAO,MAAMG,uBAAuBJ;IAClC,OAAgBK,OAAO;QACrBC,aAAaT,KAAKU,MAAM,CAAC;YACvBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,iEAAgE;IAE9F,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtBC,KAAKf,MAAMgB,OAAO,CAAC;YACjBC,SAAS;YACTP,aAAa;QACf;QACAQ,YAAYlB,MAAMS,MAAM,CAAC;YACvBQ,SAAS;YACTP,aAAa;QACf;QACA,aAAaV,MAAMgB,OAAO,CAAC;YACzBC,SAAS;YACTP,aAAa;QACf;IACF,EAAC;IAED,MAAaS,MAAqB;QAChC,MAAM,EAACZ,IAAI,EAAEO,KAAK,EAAC,GAAG,MAAM,IAAI,CAACM,KAAK,CAACd;QACvC,MAAM,EAACE,WAAW,EAAC,GAAGD;QACtB,MAAMc,UAAUC,QAAQC,GAAG;QAE3B,IAAI,CAACf,aAAa;YAChB,IAAI,CAACgB,SAAS;YACd;QACF;QAEA,6EAA6E;QAC7E,yDAAyD;QACzD,MAAMC,iBAAuC,IAAIC;QACjD,KAAK,MAAM,CAACC,cAAcC,IAAI,IAAIC,OAAOC,OAAO,CAAC1B,UAAW;YAC1DqB,eAAeM,GAAG,CAACJ,aAAaK,WAAW,IAAIJ;QACjD;QAEA,yBAAyB;QACzB,MAAMA,MAAMH,eAAeQ,GAAG,CAACzB,YAAYwB,WAAW;QACtD,IAAI,CAACJ,KAAK;YACR,IAAI,CAACM,KAAK,CAAC,CAAC,mBAAmB,EAAE1B,YAAY,WAAW,CAAC,EAAE;gBAAC2B,MAAMlC,UAAUmC,aAAa;YAAA;QAC3F;QAEA,gFAAgF;QAChF,IAAI,OAAOR,IAAIS,MAAM,KAAK,cAAc,CAACvB,KAAK,CAAC,YAAY,EAAE;YAC3D,IAAI;gBACF,MAAMc,IAAIS,MAAM,CAAC;oBAAChB;gBAAO;YAC3B,EAAE,OAAOa,OAAO;gBACd,MAAMI,MAAMJ,iBAAiBK,QAAQL,QAAQ,IAAIK,MAAMC,OAAON;gBAC9D7B,cAAc,2BAA2BiC,IAAIG,OAAO;gBACpD,IAAI,CAACP,KAAK,CAAC,CAAC,qBAAqB,EAAEI,IAAIG,OAAO,EAAE,EAAE;oBAACN,MAAMlC,UAAUmC,aAAa;gBAAA;YAClF;QACF;QAEA,8EAA8E;QAC9E,MAAMM,OAAO5B,MAAMI,UAAU,GACzBJ,MAAMI,UAAU,CAACyB,KAAK,CAAC,KAAKC,GAAG,CAAC,CAACC,MAAQA,IAAIC,IAAI,GAAGC,OAAO,CAAC,OAAO,OACnE;YAAC;YAAM;YAAM;SAAM;QAEvB,MAAMC,SAASlC,MAAMC,GAAG;QAExB,oFAAoF;QACpF,IAAI,CAACkC,SAAS;QAEd,MAAMC,WAAW,MAAMtB,IAAIuB,OAAO,GAAG;
|
|
1
|
+
{"version":3,"sources":["../../src/commands/codemod.ts"],"sourcesContent":["import {execSync, spawn} from 'node:child_process'\nimport {existsSync} from 'node:fs'\nimport path from 'node:path'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {codemods} from '../actions/codemods/index.js'\nimport {type CodeMod} from '../actions/codemods/types.js'\n\nconst codemodeDebug = subdebug('codemod')\n\nexport class CodemodCommand extends SanityCommand<typeof CodemodCommand> {\n static override args = {\n codemodName: Args.string({\n description: 'Name of the codemod to run',\n required: false,\n }),\n }\n\n static override description = 'Updates Sanity Studio codebase with a code modification script'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Show available code mods',\n },\n {\n command: '<%= config.bin %> <%= command.id %> reactIconsV3 --dry',\n description: 'Run codemod to transform react-icons imports (dry run)',\n },\n ]\n\n static override flags = {\n dry: Flags.boolean({\n default: false,\n description: 'Dry run (no changes are made to files)',\n }),\n extensions: Flags.string({\n default: 'js,ts,tsx',\n description: 'Transform files with these file extensions (comma separated)',\n }),\n 'no-verify': Flags.boolean({\n default: false,\n description: 'Skip verification steps before running codemod',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CodemodCommand)\n const {codemodName} = args\n const workDir = process.cwd()\n\n if (!codemodName) {\n this.printMods()\n return\n }\n\n // Make a record where all the keys of the available codemods are lowercased,\n // eg `reactIconsV3` turns into `{reacticonsv3: CodeMod}`\n const normalizedMods: Map<string, CodeMod> = new Map()\n for (const [originalName, mod] of Object.entries(codemods)) {\n normalizedMods.set(originalName.toLowerCase(), mod)\n }\n\n // Verify that mod exists\n const mod = normalizedMods.get(codemodName.toLowerCase())\n if (!mod) {\n this.error(`Codemod with name \"${codemodName}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n // Verify if there is any verification defined, and user has not opted out of it\n if (typeof mod.verify === 'function' && !flags['no-verify']) {\n try {\n await mod.verify({workDir})\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n codemodeDebug('Verification failed: %s', err.message)\n this.error(`Verification failed: ${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n // If extensions are defined, they come in as CSV, so split and normalize them\n const exts = flags.extensions\n ? flags.extensions.split(',').map((ext) => ext.trim().replace(/^\\./, ''))\n : ['js', 'ts', 'tsx']\n\n const dryRun = flags.dry\n\n // We use npx to run jscodeshift, so verify that it exists and works before using it\n this.ensureNpx()\n\n const prepared = await mod.prepare?.({dry: dryRun, output: this.output, workDir})\n // jscodeshift JSON-parses option values, so encode them to keep strings like \"2024\" as strings\n const transformOptions = Object.entries(prepared?.options ?? {}).map(\n ([key, value]) => `--${key}=${JSON.stringify(value)}`,\n )\n\n // If there is a gitignore in the target folder, we want to use that to know\n // which folders to ignore - eg `dist`, `coverage` or whatever\n const hasGitIgnore = existsSync(path.join(workDir, '.gitignore'))\n\n // Build the CLI command arguments\n const cliRoot = path.resolve(import.meta.dirname, '../..')\n const modPath = path.resolve(path.join(cliRoot, 'codemods', mod.filename))\n const cmdArgs = [\n 'jscodeshift',\n '--ignore-pattern',\n 'node_modules',\n '--ignore-pattern',\n 'dist',\n hasGitIgnore && '--ignore-config',\n hasGitIgnore && '.gitignore',\n '-t',\n modPath,\n '--extensions',\n exts.join(','),\n dryRun && '--dry',\n ...transformOptions,\n ...(prepared?.paths ?? [workDir]),\n ].filter((item): item is string => typeof item === 'string')\n\n const child = spawn('npx', cmdArgs, {\n stdio: 'inherit',\n })\n\n const handleSigint = () => {\n child.kill(2)\n }\n\n process.on('SIGINT', handleSigint)\n\n // Wait inside run() so oclif handles the exit, rather than throwing from an event callback\n const code = await new Promise<number | null>((resolve) => {\n child.on('close', resolve)\n })\n process.removeListener('SIGINT', handleSigint)\n this.exit(code || undefined)\n }\n\n private ensureNpx(): void {\n try {\n const npxHelp = execSync('npx --help', {encoding: 'utf8'})\n if (!npxHelp.includes('npm')) {\n this.error('Not the npx we expected', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch {\n this.error(\n `Failed to run \"npx\" - required to run codemods. Do you have a recent version of npm installed?`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n\n private printMods(): void {\n this.log('Available code modifications:\\n')\n\n for (const [modName, mod] of Object.entries(codemods)) {\n this.log(`${modName} - ${mod.purpose}`)\n }\n }\n}\n"],"names":["execSync","spawn","existsSync","path","Args","Flags","exitCodes","SanityCommand","subdebug","codemods","codemodeDebug","CodemodCommand","args","codemodName","string","description","required","examples","command","flags","dry","boolean","default","extensions","run","parse","workDir","process","cwd","printMods","normalizedMods","Map","originalName","mod","Object","entries","set","toLowerCase","get","error","exit","RUNTIME_ERROR","verify","err","Error","String","message","exts","split","map","ext","trim","replace","dryRun","ensureNpx","prepared","prepare","output","transformOptions","options","key","value","JSON","stringify","hasGitIgnore","join","cliRoot","resolve","dirname","modPath","filename","cmdArgs","paths","filter","item","child","stdio","handleSigint","kill","on","code","Promise","removeListener","undefined","npxHelp","encoding","includes","log","modName","purpose"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,KAAK,QAAO,qBAAoB;AAClD,SAAQC,UAAU,QAAO,UAAS;AAClC,OAAOC,UAAU,YAAW;AAE5B,SAAQC,IAAI,EAAEC,KAAK,QAAO,cAAa;AACvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AAEnE,SAAQC,QAAQ,QAAO,+BAA8B;AAGrD,MAAMC,gBAAgBF,SAAS;AAE/B,OAAO,MAAMG,uBAAuBJ;IAClC,OAAgBK,OAAO;QACrBC,aAAaT,KAAKU,MAAM,CAAC;YACvBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,iEAAgE;IAE9F,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtBC,KAAKf,MAAMgB,OAAO,CAAC;YACjBC,SAAS;YACTP,aAAa;QACf;QACAQ,YAAYlB,MAAMS,MAAM,CAAC;YACvBQ,SAAS;YACTP,aAAa;QACf;QACA,aAAaV,MAAMgB,OAAO,CAAC;YACzBC,SAAS;YACTP,aAAa;QACf;IACF,EAAC;IAED,MAAaS,MAAqB;QAChC,MAAM,EAACZ,IAAI,EAAEO,KAAK,EAAC,GAAG,MAAM,IAAI,CAACM,KAAK,CAACd;QACvC,MAAM,EAACE,WAAW,EAAC,GAAGD;QACtB,MAAMc,UAAUC,QAAQC,GAAG;QAE3B,IAAI,CAACf,aAAa;YAChB,IAAI,CAACgB,SAAS;YACd;QACF;QAEA,6EAA6E;QAC7E,yDAAyD;QACzD,MAAMC,iBAAuC,IAAIC;QACjD,KAAK,MAAM,CAACC,cAAcC,IAAI,IAAIC,OAAOC,OAAO,CAAC1B,UAAW;YAC1DqB,eAAeM,GAAG,CAACJ,aAAaK,WAAW,IAAIJ;QACjD;QAEA,yBAAyB;QACzB,MAAMA,MAAMH,eAAeQ,GAAG,CAACzB,YAAYwB,WAAW;QACtD,IAAI,CAACJ,KAAK;YACR,IAAI,CAACM,KAAK,CAAC,CAAC,mBAAmB,EAAE1B,YAAY,WAAW,CAAC,EAAE;gBAAC2B,MAAMlC,UAAUmC,aAAa;YAAA;QAC3F;QAEA,gFAAgF;QAChF,IAAI,OAAOR,IAAIS,MAAM,KAAK,cAAc,CAACvB,KAAK,CAAC,YAAY,EAAE;YAC3D,IAAI;gBACF,MAAMc,IAAIS,MAAM,CAAC;oBAAChB;gBAAO;YAC3B,EAAE,OAAOa,OAAO;gBACd,MAAMI,MAAMJ,iBAAiBK,QAAQL,QAAQ,IAAIK,MAAMC,OAAON;gBAC9D7B,cAAc,2BAA2BiC,IAAIG,OAAO;gBACpD,IAAI,CAACP,KAAK,CAAC,CAAC,qBAAqB,EAAEI,IAAIG,OAAO,EAAE,EAAE;oBAACN,MAAMlC,UAAUmC,aAAa;gBAAA;YAClF;QACF;QAEA,8EAA8E;QAC9E,MAAMM,OAAO5B,MAAMI,UAAU,GACzBJ,MAAMI,UAAU,CAACyB,KAAK,CAAC,KAAKC,GAAG,CAAC,CAACC,MAAQA,IAAIC,IAAI,GAAGC,OAAO,CAAC,OAAO,OACnE;YAAC;YAAM;YAAM;SAAM;QAEvB,MAAMC,SAASlC,MAAMC,GAAG;QAExB,oFAAoF;QACpF,IAAI,CAACkC,SAAS;QAEd,MAAMC,WAAW,MAAMtB,IAAIuB,OAAO,GAAG;YAACpC,KAAKiC;YAAQI,QAAQ,IAAI,CAACA,MAAM;YAAE/B;QAAO;QAC/E,+FAA+F;QAC/F,MAAMgC,mBAAmBxB,OAAOC,OAAO,CAACoB,UAAUI,WAAW,CAAC,GAAGV,GAAG,CAClE,CAAC,CAACW,KAAKC,MAAM,GAAK,CAAC,EAAE,EAAED,IAAI,CAAC,EAAEE,KAAKC,SAAS,CAACF,QAAQ;QAGvD,4EAA4E;QAC5E,8DAA8D;QAC9D,MAAMG,eAAe9D,WAAWC,KAAK8D,IAAI,CAACvC,SAAS;QAEnD,kCAAkC;QAClC,MAAMwC,UAAU/D,KAAKgE,OAAO,CAAC,YAAYC,OAAO,EAAE;QAClD,MAAMC,UAAUlE,KAAKgE,OAAO,CAAChE,KAAK8D,IAAI,CAACC,SAAS,YAAYjC,IAAIqC,QAAQ;QACxE,MAAMC,UAAU;YACd;YACA;YACA;YACA;YACA;YACAP,gBAAgB;YAChBA,gBAAgB;YAChB;YACAK;YACA;YACAtB,KAAKkB,IAAI,CAAC;YACVZ,UAAU;eACPK;eACCH,UAAUiB,SAAS;gBAAC9C;aAAQ;SACjC,CAAC+C,MAAM,CAAC,CAACC,OAAyB,OAAOA,SAAS;QAEnD,MAAMC,QAAQ1E,MAAM,OAAOsE,SAAS;YAClCK,OAAO;QACT;QAEA,MAAMC,eAAe;YACnBF,MAAMG,IAAI,CAAC;QACb;QAEAnD,QAAQoD,EAAE,CAAC,UAAUF;QAErB,2FAA2F;QAC3F,MAAMG,OAAO,MAAM,IAAIC,QAAuB,CAACd;YAC7CQ,MAAMI,EAAE,CAAC,SAASZ;QACpB;QACAxC,QAAQuD,cAAc,CAAC,UAAUL;QACjC,IAAI,CAACrC,IAAI,CAACwC,QAAQG;IACpB;IAEQ7B,YAAkB;QACxB,IAAI;YACF,MAAM8B,UAAUpF,SAAS,cAAc;gBAACqF,UAAU;YAAM;YACxD,IAAI,CAACD,QAAQE,QAAQ,CAAC,QAAQ;gBAC5B,IAAI,CAAC/C,KAAK,CAAC,2BAA2B;oBAACC,MAAMlC,UAAUmC,aAAa;gBAAA;YACtE;QACF,EAAE,OAAM;YACN,IAAI,CAACF,KAAK,CACR,CAAC,8FAA8F,CAAC,EAChG;gBAACC,MAAMlC,UAAUmC,aAAa;YAAA;QAElC;IACF;IAEQZ,YAAkB;QACxB,IAAI,CAAC0D,GAAG,CAAC;QAET,KAAK,MAAM,CAACC,SAASvD,IAAI,IAAIC,OAAOC,OAAO,CAAC1B,UAAW;YACrD,IAAI,CAAC8E,GAAG,CAAC,GAAGC,QAAQ,GAAG,EAAEvD,IAAIwD,OAAO,EAAE;QACxC;IACF;AACF"}
|
|
@@ -33,7 +33,7 @@ export class Add extends SanityCommand {
|
|
|
33
33
|
description: 'Add a production origin with credentials allowed'
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
|
-
command: '<%= config.bin %> <%= command.id %> https://myapp.com --project
|
|
36
|
+
command: '<%= config.bin %> <%= command.id %> https://myapp.com --project abc123',
|
|
37
37
|
description: 'Add a CORS origin for a specific project'
|
|
38
38
|
}
|
|
39
39
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/cors/add.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {confirm, logSymbols} from '@sanity/cli-core/ux'\nimport {oneline} from 'oneline'\n\nimport {filterAndValidateOrigin} from '../../actions/cors/filterAndValidateOrigin.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {createCorsOrigin} from '../../services/cors.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst addCorsDebug = subdebug('cors:add')\n\nexport class Add extends SanityCommand<typeof Add> {\n static override args = {\n origin: Args.string({\n description: 'Origin to allow (e.g., https://example.com)',\n required: true,\n }),\n }\n\n static override description = 'Add a CORS origin to the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively add a CORS origin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> http://localhost:3000 --no-credentials',\n description: 'Add a localhost origin without credentials',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://myapp.com --credentials',\n description: 'Add a production origin with credentials allowed',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://myapp.com --project-id abc123',\n description: 'Add a CORS origin for a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to add CORS origin to',\n semantics: 'override',\n }),\n credentials: Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Allow credentials (token/cookie) to be sent from this origin',\n required: false,\n }),\n yes: Flags.boolean({\n char: 'y',\n description: 'Confirm risky wildcard origins without prompting',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(Add)\n const {origin} = args\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'create', permission: 'sanity.project.cors'}],\n }),\n })\n\n // Check if the origin argument looks like a file path and warn. This only\n // makes sense for shell usage (unquoted globs expanding to filenames), so\n // programmatic invocations skip it — they must never probe the host's\n // filesystem.\n if (!getCliExecutionContext()) {\n try {\n const isFile = fs.existsSync(path.join(process.cwd(), args.origin))\n if (isFile) {\n this.warn(`Origin \"${args.origin}?\" Remember to quote values (sanity cors add \"*\")`)\n }\n } catch {\n // Ignore errors checking if it's a file\n }\n }\n\n const filteredOrigin = await filterAndValidateOrigin(origin, this.output)\n const hasWildcard = origin.includes('*')\n\n if (hasWildcard) {\n if (this.isUnattended() && !flags.yes) {\n this.error('Wildcard origins require confirmation. Pass `--yes` to continue.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const confirmed = flags.yes || (await this.promptForWildcardConfirmation(origin))\n if (!confirmed) {\n this.log('CORS origin not added')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n const allowCredentials =\n flags.credentials === undefined\n ? this.isUnattended()\n ? false\n : await this.promptForCredentials(hasWildcard)\n : Boolean(flags.credentials)\n\n if (filteredOrigin !== origin) {\n this.log(`Normalized origin to: ${filteredOrigin}`)\n }\n\n try {\n const response = await createCorsOrigin({\n allowCredentials,\n origin: filteredOrigin,\n projectId,\n })\n\n addCorsDebug(`CORS origin added successfully`, response)\n\n this.log('CORS origin added')\n } catch (error) {\n const err = error as Error\n\n addCorsDebug(`Error adding CORS origin`, err)\n this.error(`CORS origin addition failed:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n /**\n * Prompt the user for credentials\n *\n * @param hasWildcard - Whether the origin contains a wildcard\n * @returns - Whether to allow credentials\n */\n private async promptForCredentials(hasWildcard: boolean) {\n this.log('')\n if (hasWildcard) {\n this.log(oneline`\n ${styleText('yellow', `${logSymbols.warning} Warning:`)}\n We ${styleText(['red', 'underline'], 'HIGHLY')} recommend NOT allowing credentials\n on origins containing wildcards. If you are logged in to a Sanity studio or an app built with\n the Sanity App SDK, people will be able to send requests ${styleText('underline', 'on your behalf')}\n to read and modify data, from any matching origin. Please tread carefully!\n `)\n } else {\n this.log(oneline`\n ${styleText('yellow', `${logSymbols.warning} Warning:`)}\n Should this origin be allowed to send requests using authentication tokens or\n session cookies? Be aware that any script on this origin will be able to send\n requests ${styleText('underline', 'on your behalf')} to read and modify data if you\n are logged in to a Sanity studio or app. If this origin hosts a studio or an app built with\n the Sanity App SDK, you will need this, otherwise you should probably answer \"No\" (n).\n `)\n }\n\n this.log('')\n\n return confirm({\n default: false,\n message: oneline`\n Allow credentials to be sent from this origin? Please read the warning above.\n `,\n })\n }\n\n /**\n * Prompt the user for wildcard confirmation\n *\n * @param origin - The origin to check for wildcards\n * @returns - Whether to allow the origin\n */\n private async promptForWildcardConfirmation(origin: string) {\n this.log('')\n this.log(styleText('yellow', `${logSymbols.warning} Warning: Examples of allowed origins:`))\n\n if (origin === '*') {\n this.log('- http://www.some-malicious.site')\n this.log('- https://not.what-you-were-expecting.com')\n this.log('- https://high-traffic-site.com')\n this.log('- http://192.168.1.1:8080')\n } else {\n this.log(`- ${origin.replace(/:\\*/, ':1234').replaceAll('*', 'foo')}`)\n this.log(`- ${origin.replace(/:\\*/, ':3030').replaceAll('*', 'foo.bar')}`)\n }\n\n this.log('')\n\n return confirm({\n default: false,\n message: oneline`\n Using wildcards can be ${styleText('red', 'risky')}.\n Are you ${styleText('underline', 'absolutely sure')} you want to allow this origin?`,\n })\n }\n}\n"],"names":["fs","path","styleText","Args","Flags","exitCodes","SanityCommand","subdebug","getCliExecutionContext","confirm","logSymbols","oneline","filterAndValidateOrigin","promptForProject","createCorsOrigin","getProjectIdFlag","addCorsDebug","Add","args","origin","string","description","required","examples","command","flags","semantics","credentials","boolean","allowNo","default","undefined","yes","char","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","isFile","existsSync","join","process","cwd","warn","filteredOrigin","output","hasWildcard","includes","isUnattended","error","exit","USAGE_ERROR","confirmed","promptForWildcardConfirmation","log","USER_ABORT","allowCredentials","promptForCredentials","Boolean","response","err","message","RUNTIME_ERROR","warning","replace","replaceAll"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/cors/add.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {confirm, logSymbols} from '@sanity/cli-core/ux'\nimport {oneline} from 'oneline'\n\nimport {filterAndValidateOrigin} from '../../actions/cors/filterAndValidateOrigin.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {createCorsOrigin} from '../../services/cors.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst addCorsDebug = subdebug('cors:add')\n\nexport class Add extends SanityCommand<typeof Add> {\n static override args = {\n origin: Args.string({\n description: 'Origin to allow (e.g., https://example.com)',\n required: true,\n }),\n }\n\n static override description = 'Add a CORS origin to the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively add a CORS origin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> http://localhost:3000 --no-credentials',\n description: 'Add a localhost origin without credentials',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://myapp.com --credentials',\n description: 'Add a production origin with credentials allowed',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://myapp.com --project abc123',\n description: 'Add a CORS origin for a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to add CORS origin to',\n semantics: 'override',\n }),\n credentials: Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Allow credentials (token/cookie) to be sent from this origin',\n required: false,\n }),\n yes: Flags.boolean({\n char: 'y',\n description: 'Confirm risky wildcard origins without prompting',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(Add)\n const {origin} = args\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'create', permission: 'sanity.project.cors'}],\n }),\n })\n\n // Check if the origin argument looks like a file path and warn. This only\n // makes sense for shell usage (unquoted globs expanding to filenames), so\n // programmatic invocations skip it — they must never probe the host's\n // filesystem.\n if (!getCliExecutionContext()) {\n try {\n const isFile = fs.existsSync(path.join(process.cwd(), args.origin))\n if (isFile) {\n this.warn(`Origin \"${args.origin}?\" Remember to quote values (sanity cors add \"*\")`)\n }\n } catch {\n // Ignore errors checking if it's a file\n }\n }\n\n const filteredOrigin = await filterAndValidateOrigin(origin, this.output)\n const hasWildcard = origin.includes('*')\n\n if (hasWildcard) {\n if (this.isUnattended() && !flags.yes) {\n this.error('Wildcard origins require confirmation. Pass `--yes` to continue.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const confirmed = flags.yes || (await this.promptForWildcardConfirmation(origin))\n if (!confirmed) {\n this.log('CORS origin not added')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n const allowCredentials =\n flags.credentials === undefined\n ? this.isUnattended()\n ? false\n : await this.promptForCredentials(hasWildcard)\n : Boolean(flags.credentials)\n\n if (filteredOrigin !== origin) {\n this.log(`Normalized origin to: ${filteredOrigin}`)\n }\n\n try {\n const response = await createCorsOrigin({\n allowCredentials,\n origin: filteredOrigin,\n projectId,\n })\n\n addCorsDebug(`CORS origin added successfully`, response)\n\n this.log('CORS origin added')\n } catch (error) {\n const err = error as Error\n\n addCorsDebug(`Error adding CORS origin`, err)\n this.error(`CORS origin addition failed:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n /**\n * Prompt the user for credentials\n *\n * @param hasWildcard - Whether the origin contains a wildcard\n * @returns - Whether to allow credentials\n */\n private async promptForCredentials(hasWildcard: boolean) {\n this.log('')\n if (hasWildcard) {\n this.log(oneline`\n ${styleText('yellow', `${logSymbols.warning} Warning:`)}\n We ${styleText(['red', 'underline'], 'HIGHLY')} recommend NOT allowing credentials\n on origins containing wildcards. If you are logged in to a Sanity studio or an app built with\n the Sanity App SDK, people will be able to send requests ${styleText('underline', 'on your behalf')}\n to read and modify data, from any matching origin. Please tread carefully!\n `)\n } else {\n this.log(oneline`\n ${styleText('yellow', `${logSymbols.warning} Warning:`)}\n Should this origin be allowed to send requests using authentication tokens or\n session cookies? Be aware that any script on this origin will be able to send\n requests ${styleText('underline', 'on your behalf')} to read and modify data if you\n are logged in to a Sanity studio or app. If this origin hosts a studio or an app built with\n the Sanity App SDK, you will need this, otherwise you should probably answer \"No\" (n).\n `)\n }\n\n this.log('')\n\n return confirm({\n default: false,\n message: oneline`\n Allow credentials to be sent from this origin? Please read the warning above.\n `,\n })\n }\n\n /**\n * Prompt the user for wildcard confirmation\n *\n * @param origin - The origin to check for wildcards\n * @returns - Whether to allow the origin\n */\n private async promptForWildcardConfirmation(origin: string) {\n this.log('')\n this.log(styleText('yellow', `${logSymbols.warning} Warning: Examples of allowed origins:`))\n\n if (origin === '*') {\n this.log('- http://www.some-malicious.site')\n this.log('- https://not.what-you-were-expecting.com')\n this.log('- https://high-traffic-site.com')\n this.log('- http://192.168.1.1:8080')\n } else {\n this.log(`- ${origin.replace(/:\\*/, ':1234').replaceAll('*', 'foo')}`)\n this.log(`- ${origin.replace(/:\\*/, ':3030').replaceAll('*', 'foo.bar')}`)\n }\n\n this.log('')\n\n return confirm({\n default: false,\n message: oneline`\n Using wildcards can be ${styleText('red', 'risky')}.\n Are you ${styleText('underline', 'absolutely sure')} you want to allow this origin?`,\n })\n }\n}\n"],"names":["fs","path","styleText","Args","Flags","exitCodes","SanityCommand","subdebug","getCliExecutionContext","confirm","logSymbols","oneline","filterAndValidateOrigin","promptForProject","createCorsOrigin","getProjectIdFlag","addCorsDebug","Add","args","origin","string","description","required","examples","command","flags","semantics","credentials","boolean","allowNo","default","undefined","yes","char","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","isFile","existsSync","join","process","cwd","warn","filteredOrigin","output","hasWildcard","includes","isUnattended","error","exit","USAGE_ERROR","confirmed","promptForWildcardConfirmation","log","USER_ABORT","allowCredentials","promptForCredentials","Boolean","response","err","message","RUNTIME_ERROR","warning","replace","replaceAll"],"mappings":"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"}
|
|
@@ -23,7 +23,7 @@ export class Delete extends SanityCommand {
|
|
|
23
23
|
description: 'Delete a specific CORS origin'
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
|
-
command: '<%= config.bin %> <%= command.id %> --project
|
|
26
|
+
command: '<%= config.bin %> <%= command.id %> --project abc123',
|
|
27
27
|
description: 'Delete a CORS origin from a specific project'
|
|
28
28
|
}
|
|
29
29
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/cors/delete.ts"],"sourcesContent":["import {Args} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\n\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type CorsOrigin, deleteCorsOrigin, listCorsOrigins} from '../../services/cors.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst deleteCorsDebug = subdebug('cors:delete')\n\nexport class Delete extends SanityCommand<typeof Delete> {\n static override args = {\n origin: Args.string({\n description: 'Origin to delete (will prompt if not provided)',\n required: false,\n }),\n }\n\n static override description = 'Delete a CORS origin from the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively select and delete a CORS origin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://example.com',\n description: 'Delete a specific CORS origin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/cors/delete.ts"],"sourcesContent":["import {Args} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\n\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type CorsOrigin, deleteCorsOrigin, listCorsOrigins} from '../../services/cors.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst deleteCorsDebug = subdebug('cors:delete')\n\nexport class Delete extends SanityCommand<typeof Delete> {\n static override args = {\n origin: Args.string({\n description: 'Origin to delete (will prompt if not provided)',\n required: false,\n }),\n }\n\n static override description = 'Delete a CORS origin from the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively select and delete a CORS origin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://example.com',\n description: 'Delete a specific CORS origin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project abc123',\n description: 'Delete a CORS origin from a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to delete CORS origin from',\n semantics: 'override',\n }),\n }\n\n public async run(): Promise<void> {\n const {args} = await this.parse(Delete)\n\n if (!args.origin && this.isUnattended()) {\n this.error('Origin is required. Pass it as the `<origin>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'delete', permission: 'sanity.project.cors'}],\n }),\n })\n\n // Get the origin ID to delete\n const originId = await this.promptForOrigin(args.origin, projectId)\n\n try {\n await deleteCorsOrigin({originId, projectId})\n\n this.log('Origin deleted')\n } catch (error) {\n const err = error as Error\n deleteCorsDebug(`Error deleting CORS origin ${originId} for project ${projectId}`, err)\n this.error(`Origin deletion failed:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private async promptForOrigin(\n specifiedOrigin: string | undefined,\n projectId: string,\n ): Promise<number> {\n let origins: CorsOrigin[]\n try {\n origins = await listCorsOrigins(projectId)\n } catch (error) {\n const err = error as Error\n deleteCorsDebug(`Error fetching CORS origins for project ${projectId}`, err)\n this.error(`Failed to fetch CORS origins:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (origins.length === 0) {\n this.error('No CORS origins configured for this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n // If origin is specified, find it in the list\n if (specifiedOrigin) {\n const specifiedOriginLower = specifiedOrigin.toLowerCase()\n const selectedOrigin = origins.find(\n (origin) => origin.origin.toLowerCase() === specifiedOriginLower,\n )\n\n if (!selectedOrigin) {\n this.error(`Origin \"${specifiedOrigin}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n return selectedOrigin.id\n }\n\n // If no origin specified, prompt user to select one\n const choices = origins.map((origin) => ({\n name: origin.origin,\n value: origin.id,\n }))\n\n const selectedId = await select({\n choices,\n message: 'Select origin to delete',\n })\n\n return selectedId\n }\n}\n"],"names":["Args","exitCodes","SanityCommand","subdebug","select","promptForProject","deleteCorsOrigin","listCorsOrigins","getProjectIdFlag","deleteCorsDebug","Delete","args","origin","string","description","required","examples","command","flags","semantics","run","parse","isUnattended","error","exit","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","originId","promptForOrigin","log","err","message","RUNTIME_ERROR","specifiedOrigin","origins","length","specifiedOriginLower","toLowerCase","selectedOrigin","find","id","choices","map","name","value","selectedId"],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@ export class List extends SanityCommand {
|
|
|
11
11
|
description: 'List CORS origins for the project'
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
|
-
command: '<%= config.bin %> <%= command.id %> --project
|
|
14
|
+
command: '<%= config.bin %> <%= command.id %> --project abc123',
|
|
15
15
|
description: 'List CORS origins for a specific project'
|
|
16
16
|
}
|
|
17
17
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/cors/list.ts"],"sourcesContent":["import {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type CorsOrigin, listCorsOrigins} from '../../services/cors.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listCorsDebug = subdebug('cors:list')\n\nexport class List extends SanityCommand<typeof List> {\n static override description = 'List CORS origins for the project'\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List CORS origins for the project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/cors/list.ts"],"sourcesContent":["import {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type CorsOrigin, listCorsOrigins} from '../../services/cors.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listCorsDebug = subdebug('cors:list')\n\nexport class List extends SanityCommand<typeof List> {\n static override description = 'List CORS origins for the project'\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List CORS origins for the project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project abc123',\n description: 'List CORS origins for a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list CORS origins for',\n semantics: 'override',\n }),\n }\n\n public async run(): Promise<void> {\n await this.parse(List)\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.cors'}],\n }),\n })\n\n let origins: CorsOrigin[]\n try {\n origins = await listCorsOrigins(projectId)\n } catch (error) {\n const err = error as Error\n\n listCorsDebug(`Error fetching CORS origins for project ${projectId}`, err)\n this.error(`CORS origins list retrieval failed:\\n${err.message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (origins.length === 0) {\n this.log('No CORS origins configured for this project.')\n return\n }\n\n // Output each origin on a new line, matching the original behavior\n this.log(origins.map((origin) => origin.origin).join('\\n'))\n }\n}\n"],"names":["exitCodes","SanityCommand","subdebug","promptForProject","listCorsOrigins","getProjectIdFlag","listCorsDebug","List","description","examples","command","flags","semantics","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","origins","error","err","message","exit","RUNTIME_ERROR","length","log","map","origin","join"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AAEnE,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAyBC,eAAe,QAAO,yBAAwB;AACvE,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,gBAAgBJ,SAAS;AAE/B,OAAO,MAAMK,aAAaN;IACxB,OAAgBO,cAAc,oCAAmC;IACjE,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,OAAgBG,QAAQ;QACtB,GAAGN,iBAAiB;YAClBG,aAAa;YACbI,WAAW;QACb,EAAE;IACJ,EAAC;IAED,MAAaC,MAAqB;QAChC,MAAM,IAAI,CAACC,KAAK,CAACP;QAEjB,MAAMQ,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACRd,iBAAiB;oBACfe,qBAAqB;wBAAC;4BAACC,OAAO;4BAAQC,YAAY;wBAAqB;qBAAE;gBAC3E;QACJ;QAEA,IAAIC;QACJ,IAAI;YACFA,UAAU,MAAMjB,gBAAgBW;QAClC,EAAE,OAAOO,OAAO;YACd,MAAMC,MAAMD;YAEZhB,cAAc,CAAC,wCAAwC,EAAES,WAAW,EAAEQ;YACtE,IAAI,CAACD,KAAK,CAAC,CAAC,qCAAqC,EAAEC,IAAIC,OAAO,EAAE,EAAE;gBAChEC,MAAMzB,UAAU0B,aAAa;YAC/B;QACF;QAEA,IAAIL,QAAQM,MAAM,KAAK,GAAG;YACxB,IAAI,CAACC,GAAG,CAAC;YACT;QACF;QAEA,mEAAmE;QACnE,IAAI,CAACA,GAAG,CAACP,QAAQQ,GAAG,CAAC,CAACC,SAAWA,OAAOA,MAAM,EAAEC,IAAI,CAAC;IACvD;AACF"}
|