@sanity/cli 8.22.1 → 8.24.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.
Files changed (98) hide show
  1. package/README.md +313 -0
  2. package/dist/actions/codemods/dashboard.js +136 -8
  3. package/dist/actions/codemods/dashboard.js.map +1 -1
  4. package/dist/actions/codemods/types.js.map +1 -1
  5. package/dist/actions/context/watchJob.js +9 -3
  6. package/dist/actions/context/watchJob.js.map +1 -1
  7. package/dist/actions/deploy/blueprintManaged.js +59 -0
  8. package/dist/actions/deploy/blueprintManaged.js.map +1 -0
  9. package/dist/actions/deploy/deployApp.js +117 -1
  10. package/dist/actions/deploy/deployApp.js.map +1 -1
  11. package/dist/actions/deploy/deployRunner.js.map +1 -1
  12. package/dist/actions/deploy/deployStudio.js +6 -31
  13. package/dist/actions/deploy/deployStudio.js.map +1 -1
  14. package/dist/actions/exec/runScript.worker.js +6 -2
  15. package/dist/actions/exec/runScript.worker.js.map +1 -1
  16. package/dist/actions/init/bootstrapLocalTemplate.js +35 -11
  17. package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
  18. package/dist/actions/init/createMediaLibraryCliConfig.js +12 -0
  19. package/dist/actions/init/createMediaLibraryCliConfig.js.map +1 -0
  20. package/dist/actions/init/determineMediaLibraryTemplate.js +14 -0
  21. package/dist/actions/init/determineMediaLibraryTemplate.js.map +1 -0
  22. package/dist/actions/init/initAction.js +35 -6
  23. package/dist/actions/init/initAction.js.map +1 -1
  24. package/dist/actions/init/initApp.js +81 -6
  25. package/dist/actions/init/initApp.js.map +1 -1
  26. package/dist/actions/init/offerAspectPull.js +81 -0
  27. package/dist/actions/init/offerAspectPull.js.map +1 -0
  28. package/dist/actions/init/project/getOrCreateDataset.js +8 -5
  29. package/dist/actions/init/project/getOrCreateDataset.js.map +1 -1
  30. package/dist/actions/init/project/getOrCreateProject.js +7 -5
  31. package/dist/actions/init/project/getOrCreateProject.js.map +1 -1
  32. package/dist/actions/init/project/getProjectDetails.js +25 -2
  33. package/dist/actions/init/project/getProjectDetails.js.map +1 -1
  34. package/dist/actions/init/project/promptUserForOrganization.js +6 -4
  35. package/dist/actions/init/project/promptUserForOrganization.js.map +1 -1
  36. package/dist/actions/init/templates/index.js +3 -0
  37. package/dist/actions/init/templates/index.js.map +1 -1
  38. package/dist/actions/init/templates/mediaLibraryConfig.js +51 -0
  39. package/dist/actions/init/templates/mediaLibraryConfig.js.map +1 -0
  40. package/dist/actions/media/aspectName.js +8 -0
  41. package/dist/actions/media/aspectName.js.map +1 -0
  42. package/dist/actions/media/aspectSync/contract.js +163 -0
  43. package/dist/actions/media/aspectSync/contract.js.map +1 -0
  44. package/dist/actions/media/aspectSync/syncAspects.js +115 -0
  45. package/dist/actions/media/aspectSync/syncAspects.js.map +1 -0
  46. package/dist/actions/media/cliConfigFields.js +192 -0
  47. package/dist/actions/media/cliConfigFields.js.map +1 -0
  48. package/dist/actions/media/pullAspects.js +225 -0
  49. package/dist/actions/media/pullAspects.js.map +1 -0
  50. package/dist/actions/media/resolveMediaLibraryId.js +83 -0
  51. package/dist/actions/media/resolveMediaLibraryId.js.map +1 -0
  52. package/dist/commands/codemod.js +1 -0
  53. package/dist/commands/codemod.js.map +1 -1
  54. package/dist/commands/context/build.js +2 -2
  55. package/dist/commands/context/build.js.map +1 -1
  56. package/dist/commands/context/entries/get.js +83 -0
  57. package/dist/commands/context/entries/get.js.map +1 -0
  58. package/dist/commands/context/entries/list.js +82 -0
  59. package/dist/commands/context/entries/list.js.map +1 -0
  60. package/dist/commands/context/entries/rebuild.js +95 -0
  61. package/dist/commands/context/entries/rebuild.js.map +1 -0
  62. package/dist/commands/context/issues/apply.js +86 -0
  63. package/dist/commands/context/issues/apply.js.map +1 -0
  64. package/dist/commands/context/issues/dismiss.js +52 -0
  65. package/dist/commands/context/issues/dismiss.js.map +1 -0
  66. package/dist/commands/context/issues/get.js +105 -0
  67. package/dist/commands/context/issues/get.js.map +1 -0
  68. package/dist/commands/context/issues/list.js +128 -0
  69. package/dist/commands/context/issues/list.js.map +1 -0
  70. package/dist/commands/context/issues/reopen.js +53 -0
  71. package/dist/commands/context/issues/reopen.js.map +1 -0
  72. package/dist/commands/context/issues/resolve.js +137 -0
  73. package/dist/commands/context/issues/resolve.js.map +1 -0
  74. package/dist/commands/context/jobs/get.js +2 -2
  75. package/dist/commands/context/jobs/get.js.map +1 -1
  76. package/dist/commands/context/query.js +92 -0
  77. package/dist/commands/context/query.js.map +1 -0
  78. package/dist/commands/deploy.js +13 -0
  79. package/dist/commands/deploy.js.map +1 -1
  80. package/dist/commands/media/prune-aspect-data.js +170 -0
  81. package/dist/commands/media/prune-aspect-data.js.map +1 -0
  82. package/dist/commands/media/pull-aspects.js +309 -0
  83. package/dist/commands/media/pull-aspects.js.map +1 -0
  84. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +17 -0
  85. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  86. package/dist/services/context.js +96 -3
  87. package/dist/services/context.js.map +1 -1
  88. package/dist/services/mediaLibraries.js +175 -8
  89. package/dist/services/mediaLibraries.js.map +1 -1
  90. package/dist/util/searchChoices.js +52 -0
  91. package/dist/util/searchChoices.js.map +1 -0
  92. package/oclif.manifest.json +2134 -1445
  93. package/package.json +9 -8
  94. package/templates/media-library-config/README.md +52 -0
  95. package/templates/media-library-config/eslint.config.txt +52 -0
  96. package/templates/media-library-config/src/fields/caption.tsx +29 -0
  97. package/templates/media-library-config/src/fields/notes.tsx +54 -0
  98. package/templates/shopify/.gitignore +0 -26
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/initAction.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {\n exitCodes,\n type SanityOrgUser,\n subdebug,\n type TelemetryUserProperties,\n} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\nimport {type Framework, frameworks} from '@vercel/frameworks'\nimport deburr from 'lodash-es/deburr.js'\n\nimport {promptForConfigFiles} from '../../prompts/init/nextjs.js'\nimport {getCliUser} from '../../services/user.js'\nimport {CLIInitStepCompleted, type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {detectFrameworkRecord} from '../../util/detectFramework.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectDefaults} from '../../util/getProjectDefaults.js'\nimport {validateSession} from '../auth/ensureAuthenticated.js'\nimport {getProviderName, getUserDisplayName} from '../auth/getProviderName.js'\nimport {login} from '../auth/login/login.js'\nimport {LOGIN_REQUIRED_MESSAGE} from '../auth/login/loginInstructions.js'\nimport {detectAvailableEditors} from '../mcp/detectAvailableEditors.js'\nimport {setupMCP} from '../mcp/setupMCP.js'\nimport {setupSkills} from '../skills/setupSkills.js'\nimport {checkNextJsReactCompatibility} from './checkNextJsReactCompatibility.js'\nimport {determineAppTemplate} from './determineAppTemplate.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {initApp} from './initApp.js'\nimport {InitError} from './initError.js'\nimport {flagOrDefault, shouldPrompt, writeStagingEnvIfNeeded} from './initHelpers.js'\nimport {initNextJs} from './initNextJs.js'\nimport {initStudio} from './initStudio.js'\nimport {getPlan} from './plan/getPlan.js'\nimport {createProjectFromName} from './project/createProjectFromName.js'\nimport {getProjectDetails} from './project/getProjectDetails.js'\nimport {getProjectOutputPath} from './project/getProjectOutputPath.js'\nimport {checkIsRemoteTemplate, getGitHubRepoInfo, type RepoInfo} from './remoteTemplate.js'\nimport {type InitContext, type InitOptions} from './types.js'\n\nconst debug = subdebug('init')\n\nexport async function initAction(options: InitOptions, context: InitContext): Promise<void> {\n const {output, workDir} = context\n\n if (options.argType) {\n throw new InitError(\n options.argType === 'plugin'\n ? 'Initializing plugins through the CLI is no longer supported'\n : `Unknown init type \"${options.argType}\"`,\n 1,\n )\n }\n\n const trace = context.telemetry.trace(CLIInitStepCompleted)\n\n if (options.reconfigure) {\n throw new InitError('--reconfigure is deprecated - manual configuration is now required', 1)\n }\n\n if (options.project && options.organization) {\n throw new InitError(\n 'You have specified both a project and an organization. To move a project to an organization please visit https://www.sanity.io/manage',\n 1,\n )\n }\n\n const defaultConfig = options.datasetDefault\n let showDefaultConfigPrompt = !defaultConfig\n if (options.dataset || options.visibility || options.datasetDefault || options.unattended) {\n showDefaultConfigPrompt = false\n }\n\n const detectedFramework = await detectFrameworkRecord({\n frameworkList: frameworks as readonly Framework[],\n rootPath: workDir,\n })\n const isNextJs = detectedFramework?.slug === 'nextjs'\n\n let remoteTemplateInfo: RepoInfo | undefined\n if (options.template && checkIsRemoteTemplate(options.template)) {\n remoteTemplateInfo = await getGitHubRepoInfo(options.template, options.templateToken)\n }\n\n // Warned before any setup so the user can cancel. Remote templates ship their\n // own `sanity.cli.*`, so there is nothing to swap.\n if (options.dashboard && remoteTemplateInfo) {\n output.warn('--dashboard is ignored for remote templates')\n }\n\n if (detectedFramework && detectedFramework.slug !== 'sanity' && remoteTemplateInfo) {\n throw new InitError(\n `A remote template cannot be used with a detected framework. Detected: ${detectedFramework.name}`,\n 1,\n )\n }\n\n const isAppTemplate = options.template ? determineAppTemplate(options.template) : false\n\n if (options.unattended) {\n checkFlagsInUnattendedMode(options, {isAppTemplate, isNextJs})\n }\n\n trace.start()\n trace.log({\n flags: {\n bare: options.bare,\n coupon: options.coupon,\n defaultConfig,\n env: options.env,\n git: typeof options.git === 'string' ? options.git : undefined,\n plan: options.projectPlan,\n reconfigure: options.reconfigure,\n unattended: options.unattended,\n },\n step: 'start',\n })\n\n const planId = await getPlan(options, output, trace)\n\n let envFilenameDefault = '.env'\n if (detectedFramework && detectedFramework.slug === 'nextjs') {\n envFilenameDefault = '.env.local'\n }\n const envFilename = typeof options.env === 'string' ? options.env : envFilenameDefault\n\n const {user} = await ensureAuthenticated(options, output, trace)\n if (!isAppTemplate) {\n output.log(`${logSymbols.success} Fetching existing projects`)\n output.log('')\n }\n\n let newProject: string | undefined\n if (options.projectName) {\n newProject = await createProjectFromName({\n coupon: options.coupon,\n createProjectName: options.projectName,\n dataset: options.dataset,\n organization: options.organization,\n planId,\n user,\n visibility: options.visibility,\n })\n }\n\n const {datasetName, displayName, isFirstProject, organizationId, projectId} =\n await getProjectDetails({\n coupon: options.coupon,\n dataset: options.dataset,\n datasetDefault: options.datasetDefault,\n isAppTemplate,\n newProject,\n organization: options.organization,\n output,\n planId,\n project: options.project,\n showDefaultConfigPrompt,\n trace,\n unattended: options.unattended,\n user,\n visibility: options.visibility,\n })\n\n if (options.bare) {\n output.log(`${logSymbols.success} Below are your project details`)\n output.log('')\n output.log(`Project ID: ${styleText('cyan', projectId)}`)\n output.log(`Dataset: ${styleText('cyan', datasetName)}`)\n output.log(\n `\\nYou can find your project on Sanity Manage — https://www.sanity.io/manage/project/${projectId}\\n`,\n )\n trace.complete()\n return\n }\n\n // Detect editors once, then share the result with MCP and skills setup so\n // we don't pay the detection cost (filesystem probes + CLI execa calls) twice.\n const detectedEditors =\n options.mcpMode === 'skip' && options.skillsMode === 'skip'\n ? []\n : await detectAvailableEditors()\n\n const mcpResult = await setupMCP({\n editors: detectedEditors,\n mode: options.mcpMode,\n output,\n skillsMode: options.skillsMode,\n })\n\n trace.log({\n configuredEditors: mcpResult.configuredEditors,\n detectedEditors: mcpResult.detectedEditors,\n skipped: mcpResult.skipped,\n step: 'mcpSetup',\n })\n if (mcpResult.error) {\n trace.error(mcpResult.error)\n }\n const mcpConfigured = mcpResult.configuredEditors\n\n async function installSkills(): Promise<void> {\n if (mcpResult.skillsToInstall.length === 0) return\n try {\n const skillsResult = await setupSkills({\n agents: mcpResult.skillsToInstall,\n output,\n })\n trace.log({\n installedAgents: skillsResult.installedAgents,\n skipped: skillsResult.skipped,\n step: 'skillsSetup',\n })\n if (skillsResult.error) {\n trace.error(skillsResult.error)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n debug('Unexpected error from setupSkills %O', err)\n output.warn(`Could not install Sanity agent skills: ${err.message}`)\n trace.error(err)\n }\n }\n\n // Install skills right after the MCP/skills prompt so the progress + result\n // surface in sequence, rather than trailing the studio \"Success!\" output.\n await installSkills()\n\n const {alreadyConfiguredEditors} = mcpResult\n if (alreadyConfiguredEditors.length > 0) {\n const label =\n alreadyConfiguredEditors.length === 1\n ? `${alreadyConfiguredEditors[0]} already configured for Sanity MCP`\n : `${alreadyConfiguredEditors.length} editors already configured for Sanity MCP`\n spinner(label).start().succeed()\n }\n\n let initNext = flagOrDefault(options.nextjsAddConfigFiles, false)\n if (isNextJs && shouldPrompt(options.unattended, options.nextjsAddConfigFiles)) {\n initNext = await promptForConfigFiles()\n }\n\n trace.log({\n detectedFramework: detectedFramework?.name,\n selectedOption: initNext ? 'yes' : 'no',\n step: 'useDetectedFramework',\n })\n\n const sluggedName = deburr(displayName.toLowerCase())\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n\n const initFramework = initNext\n\n const defaults = await getProjectDefaults({isPlugin: false, workDir})\n\n const outputPath = await getProjectOutputPath({\n initFramework,\n outputPath: options.outputPath,\n sluggedName,\n unattended: options.unattended,\n useEnv: Boolean(options.env),\n workDir,\n })\n\n if (isNextJs) {\n await checkNextJsReactCompatibility({\n detectedFramework,\n output,\n outputPath,\n })\n }\n\n if (initNext) {\n if (options.dashboard) {\n output.warn('--dashboard is ignored for Next.js projects')\n }\n await initNextJs({\n datasetName,\n detectedFramework,\n envFilename,\n mcpConfigured,\n options,\n output,\n projectId,\n trace,\n workDir,\n })\n trace.complete()\n return\n }\n\n if (options.env) {\n await createOrAppendEnvVars({\n envVars: {\n DATASET: datasetName,\n PROJECT_ID: projectId,\n },\n filename: envFilename,\n framework: detectedFramework,\n log: false,\n output,\n outputPath,\n })\n await writeStagingEnvIfNeeded(output, outputPath)\n trace.complete()\n return\n }\n\n // The Dashboard is opt-in (no prompt): `--dashboard` swaps the scaffolded\n // `sanity.cli.*` over to `defineApplication` — the branded app is the sole\n // Dashboard opt-in.\n const workbench = flagOrDefault(options.dashboard, false)\n\n const sharedParams = {\n defaults,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workbench,\n workDir,\n }\n\n await (isAppTemplate\n ? initApp({...sharedParams, datasetName, projectId})\n : initStudio({\n ...sharedParams,\n datasetName,\n displayName,\n isFirstProject,\n projectId,\n }))\n\n trace.complete()\n}\n\nfunction checkFlagsInUnattendedMode(\n options: InitOptions,\n {isAppTemplate, isNextJs}: {isAppTemplate: boolean; isNextJs: boolean},\n): void {\n debug('Unattended mode, validating required options')\n\n const errors: string[] = []\n\n if (isAppTemplate) {\n if (!options.outputPath) {\n errors.push(\n 'Output path is required in unattended mode. Pass it with `--output-path <path>`.',\n )\n }\n\n if (options.projectName && !options.organization) {\n errors.push(\n 'Organization is required when creating a project in unattended mode. Pass it with `--organization <id>`.',\n )\n } else if (!options.project && !options.organization) {\n errors.push(\n 'Project or organization is required for app templates in unattended mode. ' +\n 'Pass `--project <id>` or `--organization <id>`. To create a project, pass ' +\n '`--project-name <name>` with `--organization <id>`.',\n )\n }\n } else {\n if (!isNextJs && !options.bare && !options.outputPath) {\n errors.push(\n 'Output path is required in unattended mode. Pass it with `--output-path <path>`.',\n )\n }\n\n if (!options.project && !options.projectName) {\n errors.push(\n 'Project is required in unattended mode. Pass it with `--project <id>` or `--project-name <name>`.',\n )\n }\n\n if (!options.project && !options.organization) {\n errors.push(\n 'Organization is required when creating a project in unattended mode. Pass it with `--organization <id>`.',\n )\n }\n }\n\n if (errors.length > 0) {\n throw new InitError(formatCliErrorMessages(errors), exitCodes.USAGE_ERROR)\n }\n}\n\nasync function ensureAuthenticated(\n options: InitOptions,\n output: InitContext['output'],\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>,\n): Promise<{user: SanityOrgUser}> {\n const user = await validateSession()\n\n if (user) {\n trace.log({alreadyLoggedIn: true, step: 'login'})\n output.log(\n `${logSymbols.success} You are logged in as ${getUserDisplayName(user)} using ${getProviderName(user.provider)}`,\n )\n return {user}\n }\n\n if (options.unattended) {\n throw new InitError(LOGIN_REQUIRED_MESSAGE, exitCodes.RUNTIME_ERROR)\n }\n\n trace.log({step: 'login'})\n output.warn(LOGIN_REQUIRED_MESSAGE)\n\n try {\n await login({\n output,\n telemetry: trace.newContext('login'),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new InitError(`Login failed: ${message}`, 1)\n }\n\n const loggedInUser = await getCliUser()\n\n output.log(\n `${logSymbols.success} You are logged in as ${getUserDisplayName(loggedInUser)} using ${getProviderName(loggedInUser.provider)}`,\n )\n return {user: loggedInUser}\n}\n"],"names":["styleText","exitCodes","subdebug","logSymbols","spinner","frameworks","deburr","promptForConfigFiles","getCliUser","CLIInitStepCompleted","detectFrameworkRecord","formatCliErrorMessages","getProjectDefaults","validateSession","getProviderName","getUserDisplayName","login","LOGIN_REQUIRED_MESSAGE","detectAvailableEditors","setupMCP","setupSkills","checkNextJsReactCompatibility","determineAppTemplate","createOrAppendEnvVars","initApp","InitError","flagOrDefault","shouldPrompt","writeStagingEnvIfNeeded","initNextJs","initStudio","getPlan","createProjectFromName","getProjectDetails","getProjectOutputPath","checkIsRemoteTemplate","getGitHubRepoInfo","debug","initAction","options","context","output","workDir","argType","trace","telemetry","reconfigure","project","organization","defaultConfig","datasetDefault","showDefaultConfigPrompt","dataset","visibility","unattended","detectedFramework","frameworkList","rootPath","isNextJs","slug","remoteTemplateInfo","template","templateToken","dashboard","warn","name","isAppTemplate","checkFlagsInUnattendedMode","start","log","flags","bare","coupon","env","git","undefined","plan","projectPlan","step","planId","envFilenameDefault","envFilename","user","ensureAuthenticated","success","newProject","projectName","createProjectName","datasetName","displayName","isFirstProject","organizationId","projectId","complete","detectedEditors","mcpMode","skillsMode","mcpResult","editors","mode","configuredEditors","skipped","error","mcpConfigured","installSkills","skillsToInstall","length","skillsResult","agents","installedAgents","err","Error","String","message","alreadyConfiguredEditors","label","succeed","initNext","nextjsAddConfigFiles","selectedOption","sluggedName","toLowerCase","replaceAll","initFramework","defaults","isPlugin","outputPath","useEnv","Boolean","envVars","DATASET","PROJECT_ID","filename","framework","workbench","sharedParams","errors","push","USAGE_ERROR","alreadyLoggedIn","provider","RUNTIME_ERROR","newContext","loggedInUser"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/init/initAction.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {\n exitCodes,\n type SanityOrgUser,\n subdebug,\n type TelemetryUserProperties,\n} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\nimport {type Framework, frameworks} from '@vercel/frameworks'\nimport deburr from 'lodash-es/deburr.js'\n\nimport {promptForConfigFiles} from '../../prompts/init/nextjs.js'\nimport {getCliUser} from '../../services/user.js'\nimport {CLIInitStepCompleted, type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {detectFrameworkRecord} from '../../util/detectFramework.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectDefaults} from '../../util/getProjectDefaults.js'\nimport {validateSession} from '../auth/ensureAuthenticated.js'\nimport {getProviderName, getUserDisplayName} from '../auth/getProviderName.js'\nimport {login} from '../auth/login/login.js'\nimport {LOGIN_REQUIRED_MESSAGE} from '../auth/login/loginInstructions.js'\nimport {detectAvailableEditors} from '../mcp/detectAvailableEditors.js'\nimport {setupMCP} from '../mcp/setupMCP.js'\nimport {setupSkills} from '../skills/setupSkills.js'\nimport {checkNextJsReactCompatibility} from './checkNextJsReactCompatibility.js'\nimport {determineAppTemplate} from './determineAppTemplate.js'\nimport {determineMediaLibraryTemplate} from './determineMediaLibraryTemplate.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {initApp} from './initApp.js'\nimport {InitError} from './initError.js'\nimport {flagOrDefault, shouldPrompt, writeStagingEnvIfNeeded} from './initHelpers.js'\nimport {initNextJs} from './initNextJs.js'\nimport {initStudio} from './initStudio.js'\nimport {getPlan} from './plan/getPlan.js'\nimport {createProjectFromName} from './project/createProjectFromName.js'\nimport {getProjectDetails} from './project/getProjectDetails.js'\nimport {getProjectOutputPath} from './project/getProjectOutputPath.js'\nimport {checkIsRemoteTemplate, getGitHubRepoInfo, type RepoInfo} from './remoteTemplate.js'\nimport {type InitContext, type InitOptions} from './types.js'\n\nconst debug = subdebug('init')\n\nexport async function initAction(options: InitOptions, context: InitContext): Promise<void> {\n const {output, workDir} = context\n\n if (options.argType) {\n throw new InitError(\n options.argType === 'plugin'\n ? 'Initializing plugins through the CLI is no longer supported'\n : `Unknown init type \"${options.argType}\"`,\n 1,\n )\n }\n\n const trace = context.telemetry.trace(CLIInitStepCompleted)\n\n if (options.reconfigure) {\n throw new InitError('--reconfigure is deprecated - manual configuration is now required', 1)\n }\n\n // Media Library config templates ride the app init flow end to end:\n // organization-only project details, app unattended-flag rules, `initApp`.\n // Template-shape differences are handled downstream (bootstrapLocalTemplate\n // and initApp check `determineMediaLibraryTemplate` themselves).\n const isMediaLibraryTemplate = options.template\n ? determineMediaLibraryTemplate(options.template)\n : false\n\n // That template ignores project flags outright (getProjectDetails warns), so\n // the pair is not the move-a-project contradiction this guard is about.\n if (options.project && options.organization && !isMediaLibraryTemplate) {\n throw new InitError(\n 'You have specified both a project and an organization. To move a project to an organization please visit https://www.sanity.io/manage',\n 1,\n )\n }\n\n const defaultConfig = options.datasetDefault\n let showDefaultConfigPrompt = !defaultConfig\n if (options.dataset || options.visibility || options.datasetDefault || options.unattended) {\n showDefaultConfigPrompt = false\n }\n\n const detectedFramework = await detectFrameworkRecord({\n frameworkList: frameworks as readonly Framework[],\n rootPath: workDir,\n })\n const isNextJs = detectedFramework?.slug === 'nextjs'\n\n let remoteTemplateInfo: RepoInfo | undefined\n if (options.template && checkIsRemoteTemplate(options.template)) {\n remoteTemplateInfo = await getGitHubRepoInfo(options.template, options.templateToken)\n }\n\n // Warned before any setup so the user can cancel. Remote templates ship their\n // own `sanity.cli.*`, so there is nothing to swap.\n if (options.dashboard && remoteTemplateInfo) {\n output.warn('--dashboard is ignored for remote templates')\n }\n\n if (detectedFramework && detectedFramework.slug !== 'sanity' && remoteTemplateInfo) {\n throw new InitError(\n `A remote template cannot be used with a detected framework. Detected: ${detectedFramework.name}`,\n 1,\n )\n }\n\n const isAppTemplate = options.template\n ? determineAppTemplate(options.template) || isMediaLibraryTemplate\n : false\n\n // A config app persists to the org's installation, not to a dashboard\n // workbench, so `createMediaLibraryCliConfig` has no slot for --dashboard.\n // Warned rather than silently dropped (same courtesy as remote templates).\n if (options.dashboard && isMediaLibraryTemplate) {\n output.warn('--dashboard is ignored for the media-library-config template')\n }\n\n if (options.unattended) {\n checkFlagsInUnattendedMode(options, {isAppTemplate, isMediaLibraryTemplate, isNextJs})\n }\n\n trace.start()\n trace.log({\n flags: {\n bare: options.bare,\n coupon: options.coupon,\n defaultConfig,\n env: options.env,\n git: typeof options.git === 'string' ? options.git : undefined,\n plan: options.projectPlan,\n reconfigure: options.reconfigure,\n unattended: options.unattended,\n },\n step: 'start',\n })\n\n const planId = await getPlan(options, output, trace)\n\n let envFilenameDefault = '.env'\n if (detectedFramework && detectedFramework.slug === 'nextjs') {\n envFilenameDefault = '.env.local'\n }\n const envFilename = typeof options.env === 'string' ? options.env : envFilenameDefault\n\n const {user} = await ensureAuthenticated(options, output, trace)\n if (!isAppTemplate) {\n output.log(`${logSymbols.success} Fetching existing projects`)\n output.log('')\n }\n\n let newProject: string | undefined\n // A media-library config records only the organization; creating a project\n // from --project-name here would be a throwaway side effect.\n if (options.projectName && isMediaLibraryTemplate) {\n output.warn(\n '--project-name is ignored for the media-library-config template — it only records the organization.',\n )\n }\n if (options.projectName && !isMediaLibraryTemplate) {\n newProject = await createProjectFromName({\n coupon: options.coupon,\n createProjectName: options.projectName,\n dataset: options.dataset,\n organization: options.organization,\n planId,\n user,\n visibility: options.visibility,\n })\n }\n\n const {datasetName, displayName, isFirstProject, organizationId, projectId} =\n await getProjectDetails({\n coupon: options.coupon,\n dataset: options.dataset,\n datasetDefault: options.datasetDefault,\n isAppTemplate,\n isMediaLibraryTemplate,\n newProject,\n organization: options.organization,\n output,\n planId,\n project: options.project,\n showDefaultConfigPrompt,\n trace,\n unattended: options.unattended,\n user,\n visibility: options.visibility,\n })\n\n if (options.bare) {\n output.log(`${logSymbols.success} Below are your project details`)\n output.log('')\n output.log(`Project ID: ${styleText('cyan', projectId)}`)\n output.log(`Dataset: ${styleText('cyan', datasetName)}`)\n output.log(\n `\\nYou can find your project on Sanity Manage — https://www.sanity.io/manage/project/${projectId}\\n`,\n )\n trace.complete()\n return\n }\n\n // Detect editors once, then share the result with MCP and skills setup so\n // we don't pay the detection cost (filesystem probes + CLI execa calls) twice.\n const detectedEditors =\n options.mcpMode === 'skip' && options.skillsMode === 'skip'\n ? []\n : await detectAvailableEditors()\n\n const mcpResult = await setupMCP({\n editors: detectedEditors,\n mode: options.mcpMode,\n output,\n skillsMode: options.skillsMode,\n })\n\n trace.log({\n configuredEditors: mcpResult.configuredEditors,\n detectedEditors: mcpResult.detectedEditors,\n skipped: mcpResult.skipped,\n step: 'mcpSetup',\n })\n if (mcpResult.error) {\n trace.error(mcpResult.error)\n }\n const mcpConfigured = mcpResult.configuredEditors\n\n async function installSkills(): Promise<void> {\n if (mcpResult.skillsToInstall.length === 0) return\n try {\n const skillsResult = await setupSkills({\n agents: mcpResult.skillsToInstall,\n output,\n })\n trace.log({\n installedAgents: skillsResult.installedAgents,\n skipped: skillsResult.skipped,\n step: 'skillsSetup',\n })\n if (skillsResult.error) {\n trace.error(skillsResult.error)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n debug('Unexpected error from setupSkills %O', err)\n output.warn(`Could not install Sanity agent skills: ${err.message}`)\n trace.error(err)\n }\n }\n\n // Install skills right after the MCP/skills prompt so the progress + result\n // surface in sequence, rather than trailing the studio \"Success!\" output.\n await installSkills()\n\n const {alreadyConfiguredEditors} = mcpResult\n if (alreadyConfiguredEditors.length > 0) {\n const label =\n alreadyConfiguredEditors.length === 1\n ? `${alreadyConfiguredEditors[0]} already configured for Sanity MCP`\n : `${alreadyConfiguredEditors.length} editors already configured for Sanity MCP`\n spinner(label).start().succeed()\n }\n\n let initNext = flagOrDefault(options.nextjsAddConfigFiles, false)\n if (isNextJs && shouldPrompt(options.unattended, options.nextjsAddConfigFiles)) {\n initNext = await promptForConfigFiles()\n }\n\n trace.log({\n detectedFramework: detectedFramework?.name,\n selectedOption: initNext ? 'yes' : 'no',\n step: 'useDetectedFramework',\n })\n\n // The media-library config template skips project selection, so\n // `displayName` is empty and the slug would be too — which would default the\n // output-path prompt to the (usually non-empty) cwd, a default\n // `validateEmptyPath` then refuses. Fall back to the template's own name.\n const sluggedName =\n deburr(displayName.toLowerCase())\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '') || (isMediaLibraryTemplate ? 'media-library-config' : '')\n\n const initFramework = initNext\n\n const defaults = await getProjectDefaults({isPlugin: false, workDir})\n\n const outputPath = await getProjectOutputPath({\n initFramework,\n outputPath: options.outputPath,\n sluggedName,\n unattended: options.unattended,\n useEnv: Boolean(options.env),\n workDir,\n })\n\n if (isNextJs) {\n await checkNextJsReactCompatibility({\n detectedFramework,\n output,\n outputPath,\n })\n }\n\n if (initNext) {\n if (options.dashboard) {\n output.warn('--dashboard is ignored for Next.js projects')\n }\n await initNextJs({\n datasetName,\n detectedFramework,\n envFilename,\n mcpConfigured,\n options,\n output,\n projectId,\n trace,\n workDir,\n })\n trace.complete()\n return\n }\n\n if (options.env) {\n await createOrAppendEnvVars({\n envVars: {\n DATASET: datasetName,\n PROJECT_ID: projectId,\n },\n filename: envFilename,\n framework: detectedFramework,\n log: false,\n output,\n outputPath,\n })\n await writeStagingEnvIfNeeded(output, outputPath)\n trace.complete()\n return\n }\n\n // The Dashboard is opt-in (no prompt): `--dashboard` swaps the scaffolded\n // `sanity.cli.*` over to `defineApplication` — the branded app is the sole\n // Dashboard opt-in.\n const workbench = flagOrDefault(options.dashboard, false)\n\n const sharedParams = {\n defaults,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workbench,\n workDir,\n }\n\n await (isAppTemplate\n ? initApp({...sharedParams, datasetName, projectId})\n : initStudio({\n ...sharedParams,\n datasetName,\n displayName,\n isFirstProject,\n projectId,\n }))\n\n trace.complete()\n}\n\nfunction checkFlagsInUnattendedMode(\n options: InitOptions,\n {\n isAppTemplate,\n isMediaLibraryTemplate,\n isNextJs,\n }: {isAppTemplate: boolean; isMediaLibraryTemplate: boolean; isNextJs: boolean},\n): void {\n debug('Unattended mode, validating required options')\n\n const errors: string[] = []\n\n if (isAppTemplate) {\n if (!options.outputPath) {\n errors.push(\n 'Output path is required in unattended mode. Pass it with `--output-path <path>`.',\n )\n }\n\n // The media-library config is organization-only, so project flags can't\n // stand in for --organization the way they can for other app templates.\n if (isMediaLibraryTemplate && !options.organization) {\n errors.push(\n 'Organization is required for the media-library-config template in unattended mode. ' +\n 'Pass it with `--organization <id>`.',\n )\n } else if (options.projectName && !options.organization) {\n errors.push(\n 'Organization is required when creating a project in unattended mode. Pass it with `--organization <id>`.',\n )\n } else if (!options.project && !options.organization) {\n errors.push(\n 'Project or organization is required for app templates in unattended mode. ' +\n 'Pass `--project <id>` or `--organization <id>`. To create a project, pass ' +\n '`--project-name <name>` with `--organization <id>`.',\n )\n }\n } else {\n if (!isNextJs && !options.bare && !options.outputPath) {\n errors.push(\n 'Output path is required in unattended mode. Pass it with `--output-path <path>`.',\n )\n }\n\n if (!options.project && !options.projectName) {\n errors.push(\n 'Project is required in unattended mode. Pass it with `--project <id>` or `--project-name <name>`.',\n )\n }\n\n if (!options.project && !options.organization) {\n errors.push(\n 'Organization is required when creating a project in unattended mode. Pass it with `--organization <id>`.',\n )\n }\n }\n\n if (errors.length > 0) {\n throw new InitError(formatCliErrorMessages(errors), exitCodes.USAGE_ERROR)\n }\n}\n\nasync function ensureAuthenticated(\n options: InitOptions,\n output: InitContext['output'],\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>,\n): Promise<{user: SanityOrgUser}> {\n const user = await validateSession()\n\n if (user) {\n trace.log({alreadyLoggedIn: true, step: 'login'})\n output.log(\n `${logSymbols.success} You are logged in as ${getUserDisplayName(user)} using ${getProviderName(user.provider)}`,\n )\n return {user}\n }\n\n if (options.unattended) {\n throw new InitError(LOGIN_REQUIRED_MESSAGE, exitCodes.RUNTIME_ERROR)\n }\n\n trace.log({step: 'login'})\n output.warn(LOGIN_REQUIRED_MESSAGE)\n\n try {\n await login({\n output,\n telemetry: trace.newContext('login'),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new InitError(`Login failed: ${message}`, 1)\n }\n\n const loggedInUser = await getCliUser()\n\n output.log(\n `${logSymbols.success} You are logged in as ${getUserDisplayName(loggedInUser)} using ${getProviderName(loggedInUser.provider)}`,\n )\n return {user: loggedInUser}\n}\n"],"names":["styleText","exitCodes","subdebug","logSymbols","spinner","frameworks","deburr","promptForConfigFiles","getCliUser","CLIInitStepCompleted","detectFrameworkRecord","formatCliErrorMessages","getProjectDefaults","validateSession","getProviderName","getUserDisplayName","login","LOGIN_REQUIRED_MESSAGE","detectAvailableEditors","setupMCP","setupSkills","checkNextJsReactCompatibility","determineAppTemplate","determineMediaLibraryTemplate","createOrAppendEnvVars","initApp","InitError","flagOrDefault","shouldPrompt","writeStagingEnvIfNeeded","initNextJs","initStudio","getPlan","createProjectFromName","getProjectDetails","getProjectOutputPath","checkIsRemoteTemplate","getGitHubRepoInfo","debug","initAction","options","context","output","workDir","argType","trace","telemetry","reconfigure","isMediaLibraryTemplate","template","project","organization","defaultConfig","datasetDefault","showDefaultConfigPrompt","dataset","visibility","unattended","detectedFramework","frameworkList","rootPath","isNextJs","slug","remoteTemplateInfo","templateToken","dashboard","warn","name","isAppTemplate","checkFlagsInUnattendedMode","start","log","flags","bare","coupon","env","git","undefined","plan","projectPlan","step","planId","envFilenameDefault","envFilename","user","ensureAuthenticated","success","newProject","projectName","createProjectName","datasetName","displayName","isFirstProject","organizationId","projectId","complete","detectedEditors","mcpMode","skillsMode","mcpResult","editors","mode","configuredEditors","skipped","error","mcpConfigured","installSkills","skillsToInstall","length","skillsResult","agents","installedAgents","err","Error","String","message","alreadyConfiguredEditors","label","succeed","initNext","nextjsAddConfigFiles","selectedOption","sluggedName","toLowerCase","replaceAll","initFramework","defaults","isPlugin","outputPath","useEnv","Boolean","envVars","DATASET","PROJECT_ID","filename","framework","workbench","sharedParams","errors","push","USAGE_ERROR","alreadyLoggedIn","provider","RUNTIME_ERROR","newContext","loggedInUser"],"mappings":"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"}
@@ -1,8 +1,12 @@
1
1
  import { styleText } from 'node:util';
2
+ import { isStaging } from '@sanity/cli-core/util';
2
3
  import { logSymbols } from '@sanity/cli-core/ux';
4
+ import { getMediaLibrariesForOrganization } from '../../services/mediaLibraries.js';
3
5
  import { getInstallCommand } from '../../util/packageManager/installPackages.js';
6
+ import { determineMediaLibraryTemplate } from './determineMediaLibraryTemplate.js';
4
7
  import { InitError } from './initError.js';
5
8
  import { getPostInitMCPPrompt } from './initHelpers.js';
9
+ import { offerAspectPull } from './offerAspectPull.js';
6
10
  import { scaffoldAndInstall, selectTemplate } from './scaffoldTemplate.js';
7
11
  export async function initApp({ datasetName, defaults, mcpConfigured, options, organizationId, output, outputPath, projectId, remoteTemplateInfo, sluggedName, trace, workbench, workDir }) {
8
12
  const { template: resolvedTemplate, templateName, useTypeScript } = await selectTemplate({
@@ -32,6 +36,50 @@ export async function initApp({ datasetName, defaults, mcpConfigured, options, o
32
36
  });
33
37
  const isCurrentDir = outputPath === workDir;
34
38
  const goToProjectDir = `\n(${styleText('cyan', `cd ${outputPath}`)} to navigate to your new project directory)`;
39
+ if (determineMediaLibraryTemplate(templateName)) {
40
+ output.log(`${logSymbols.success} ${styleText([
41
+ 'green',
42
+ 'bold'
43
+ ], 'Success!')} Your Media Library config app has been scaffolded.`);
44
+ if (!isCurrentDir) output.log(goToProjectDir);
45
+ if (!options.install) {
46
+ output.log(`\nRun ${styleText('cyan', getInstallCommand(pkgManager))} to install dependencies.`);
47
+ }
48
+ let libraries = [];
49
+ if (organizationId) {
50
+ output.log(`\nConfigured for organization ${styleText('cyan', organizationId)}.`);
51
+ libraries = await warnIfNoActiveMediaLibrary(organizationId, output);
52
+ }
53
+ // The deploy mirrors config into the library and deletes stored aspects
54
+ // the config does not declare, so an org that already defines aspects is
55
+ // offered a pull now — while init is interactive and authenticated. Only
56
+ // with exactly one active library; picking one is pull-aspects territory.
57
+ let pulledAspects = false;
58
+ if (libraries.length === 1 && !options.unattended) {
59
+ output.log('');
60
+ pulledAspects = await offerAspectPull({
61
+ library: libraries[0],
62
+ output,
63
+ outputPath
64
+ });
65
+ }
66
+ output.log('\nField definitions live in `src/fields/` and are registered in `sanity.cli.ts` — see the README for the authoring rules.');
67
+ if (libraries.length > 0 && !pulledAspects) {
68
+ output.log(`\nAlready defining aspects in your Media Library? Generate field modules from them with ${styleText('cyan', 'npx sanity media pull-aspects')}.`);
69
+ }
70
+ output.log('\nRefer to our documentation for a walkthrough:');
71
+ output.log(styleText([
72
+ 'blue',
73
+ 'underline'
74
+ ], 'https://www.sanity.io/docs/media-library'));
75
+ await logMcpConfirmation(mcpConfigured, output);
76
+ output.log('\n');
77
+ output.log(`Other helpful commands:`);
78
+ output.log(`npx sanity docs browse to open the documentation in a browser`);
79
+ output.log(`npx sanity dev to serve your fields to the Media Library live`);
80
+ output.log(`npx sanity deploy to deploy the config to your organization`);
81
+ return;
82
+ }
35
83
  //output for custom apps here
36
84
  output.log(`${logSymbols.success} ${styleText([
37
85
  'green',
@@ -52,17 +100,44 @@ export async function initApp({ datasetName, defaults, mcpConfigured, options, o
52
100
  'blue',
53
101
  'underline'
54
102
  ], 'https://www.sanity.io/docs/app-sdk/sdk-configuration'));
55
- if (mcpConfigured && mcpConfigured.length > 0) {
56
- const message = await getPostInitMCPPrompt(mcpConfigured);
57
- output.log(`\n${message}`);
58
- output.log(`\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`);
59
- output.log(`\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`);
60
- }
103
+ await logMcpConfirmation(mcpConfigured, output);
61
104
  output.log('\n');
62
105
  output.log(`Other helpful commands:`);
63
106
  output.log(`npx sanity docs browse to open the documentation in a browser`);
64
107
  output.log(`npx sanity dev to start the development server for your app`);
65
108
  output.log(`npx sanity deploy to deploy your app`);
66
109
  }
110
+ /**
111
+ * The post-init MCP confirmation shown by every template branch: which
112
+ * editors were configured during init and where to learn more. No-op when
113
+ * MCP setup was skipped or configured nothing.
114
+ */ async function logMcpConfirmation(mcpConfigured, output) {
115
+ if (!mcpConfigured || mcpConfigured.length === 0) return;
116
+ const message = await getPostInitMCPPrompt(mcpConfigured);
117
+ output.log(`\n${message}`);
118
+ output.log(`\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`);
119
+ output.log(`\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`);
120
+ }
121
+ /**
122
+ * Media Library is provisioned per organization, outside this app (in
123
+ * Manage). Without an active library the dashboard's media area comes up
124
+ * empty, so say so now — init is the one interactive, authenticated moment.
125
+ * API errors (offline, no access to the org, timeout) are swallowed: this is
126
+ * a hint, not a gate.
127
+ *
128
+ * @returns the organization's active libraries — empty also on lookup
129
+ * failure, so follow-up suggestions stay silent in the unknown case.
130
+ */ export async function warnIfNoActiveMediaLibrary(organizationId, output) {
131
+ try {
132
+ const libraries = await getMediaLibrariesForOrganization(organizationId);
133
+ if (libraries.length > 0) return libraries;
134
+ } catch {
135
+ return [];
136
+ }
137
+ const manageUrl = `${isStaging() ? 'https://sanity.work' : 'https://www.sanity.io'}/manage`;
138
+ // No symbol or leading newline: output.warn already prefixes `Warning:`.
139
+ output.warn(`Organization ${organizationId} has no active Media Library, ` + `so the dashboard's media area will be empty. Media Library is provisioned per ` + `organization — enable it in Manage (${manageUrl}) or learn more at ` + `https://www.sanity.io/docs/media-library.`);
140
+ return [];
141
+ }
67
142
 
68
143
  //# sourceMappingURL=initApp.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/initApp.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type Output, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\n\nimport {type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {getInstallCommand} from '../../util/packageManager/installPackages.js'\nimport {type EditorName} from '../mcp/editorConfigs.js'\nimport {InitError} from './initError.js'\nimport {getPostInitMCPPrompt} from './initHelpers.js'\nimport {type RepoInfo} from './remoteTemplate.js'\nimport {scaffoldAndInstall, selectTemplate} from './scaffoldTemplate.js'\nimport {type InitOptions} from './types.js'\n\nexport async function initApp({\n datasetName,\n defaults,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workbench,\n workDir,\n}: {\n datasetName: string\n defaults: {projectName: string}\n mcpConfigured: EditorName[]\n options: InitOptions\n organizationId: string | undefined\n output: Output\n outputPath: string\n projectId: string\n remoteTemplateInfo: RepoInfo | undefined\n sluggedName: string\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>\n workbench: boolean\n workDir: string\n}): Promise<void> {\n const {\n template: resolvedTemplate,\n templateName,\n useTypeScript,\n } = await selectTemplate({\n options,\n remoteTemplateInfo,\n trace,\n })\n\n if (!remoteTemplateInfo && !resolvedTemplate) {\n throw new InitError(`Template \"${templateName}\" not found`, 1)\n }\n\n const {pkgManager} = await scaffoldAndInstall({\n datasetName,\n defaults,\n displayName: '',\n options,\n organizationId,\n output,\n outputPath,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n templateName,\n trace,\n useTypeScript,\n workbench,\n workDir,\n })\n\n const isCurrentDir = outputPath === workDir\n const goToProjectDir = `\\n(${styleText('cyan', `cd ${outputPath}`)} to navigate to your new project directory)`\n\n //output for custom apps here\n output.log(\n `${logSymbols.success} ${styleText(['green', 'bold'], 'Success!')} Your custom app has been scaffolded.`,\n )\n if (!isCurrentDir) output.log(goToProjectDir)\n if (!options.install) {\n output.log(`\\nRun ${styleText('cyan', getInstallCommand(pkgManager))} to install dependencies.`)\n }\n\n if (projectId && datasetName) {\n output.log(\n `\\nConfigured with project ${styleText('cyan', projectId)} and dataset ${styleText('cyan', datasetName)}.`,\n )\n output.log(\n 'Edit `src/App.tsx` to change these values or add more project / dataset pairs to your config.',\n )\n } else {\n output.log(\n `\\n${styleText('bold', 'Next')}, configure the project(s) and dataset(s) your app should work with in \\`src/App.tsx\\`.`,\n )\n }\n output.log('\\nRefer to our documentation for a walkthrough:')\n output.log(\n styleText(['blue', 'underline'], 'https://www.sanity.io/docs/app-sdk/sdk-configuration'),\n )\n if (mcpConfigured && mcpConfigured.length > 0) {\n const message = await getPostInitMCPPrompt(mcpConfigured)\n output.log(`\\n${message}`)\n output.log(`\\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`)\n output.log(\n `\\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`,\n )\n }\n output.log('\\n')\n output.log(`Other helpful commands:`)\n output.log(`npx sanity docs browse to open the documentation in a browser`)\n output.log(`npx sanity dev to start the development server for your app`)\n output.log(`npx sanity deploy to deploy your app`)\n}\n"],"names":["styleText","logSymbols","getInstallCommand","InitError","getPostInitMCPPrompt","scaffoldAndInstall","selectTemplate","initApp","datasetName","defaults","mcpConfigured","options","organizationId","output","outputPath","projectId","remoteTemplateInfo","sluggedName","trace","workbench","workDir","template","resolvedTemplate","templateName","useTypeScript","pkgManager","displayName","isCurrentDir","goToProjectDir","log","success","install","length","message"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAGnC,SAAQC,UAAU,QAAO,sBAAqB;AAI9C,SAAQC,iBAAiB,QAAO,+CAA8C;AAE9E,SAAQC,SAAS,QAAO,iBAAgB;AACxC,SAAQC,oBAAoB,QAAO,mBAAkB;AAErD,SAAQC,kBAAkB,EAAEC,cAAc,QAAO,wBAAuB;AAGxE,OAAO,eAAeC,QAAQ,EAC5BC,WAAW,EACXC,QAAQ,EACRC,aAAa,EACbC,OAAO,EACPC,cAAc,EACdC,MAAM,EACNC,UAAU,EACVC,SAAS,EACTC,kBAAkB,EAClBC,WAAW,EACXC,KAAK,EACLC,SAAS,EACTC,OAAO,EAeR;IACC,MAAM,EACJC,UAAUC,gBAAgB,EAC1BC,YAAY,EACZC,aAAa,EACd,GAAG,MAAMlB,eAAe;QACvBK;QACAK;QACAE;IACF;IAEA,IAAI,CAACF,sBAAsB,CAACM,kBAAkB;QAC5C,MAAM,IAAInB,UAAU,CAAC,UAAU,EAAEoB,aAAa,WAAW,CAAC,EAAE;IAC9D;IAEA,MAAM,EAACE,UAAU,EAAC,GAAG,MAAMpB,mBAAmB;QAC5CG;QACAC;QACAiB,aAAa;QACbf;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAM;QACAL;QACAM;QACAL;QACAC;IACF;IAEA,MAAMO,eAAeb,eAAeM;IACpC,MAAMQ,iBAAiB,CAAC,GAAG,EAAE5B,UAAU,QAAQ,CAAC,GAAG,EAAEc,YAAY,EAAE,2CAA2C,CAAC;IAE/G,6BAA6B;IAC7BD,OAAOgB,GAAG,CACR,GAAG5B,WAAW6B,OAAO,CAAC,CAAC,EAAE9B,UAAU;QAAC;QAAS;KAAO,EAAE,YAAY,qCAAqC,CAAC;IAE1G,IAAI,CAAC2B,cAAcd,OAAOgB,GAAG,CAACD;IAC9B,IAAI,CAACjB,QAAQoB,OAAO,EAAE;QACpBlB,OAAOgB,GAAG,CAAC,CAAC,MAAM,EAAE7B,UAAU,QAAQE,kBAAkBuB,aAAa,yBAAyB,CAAC;IACjG;IAEA,IAAIV,aAAaP,aAAa;QAC5BK,OAAOgB,GAAG,CACR,CAAC,0BAA0B,EAAE7B,UAAU,QAAQe,WAAW,aAAa,EAAEf,UAAU,QAAQQ,aAAa,CAAC,CAAC;QAE5GK,OAAOgB,GAAG,CACR;IAEJ,OAAO;QACLhB,OAAOgB,GAAG,CACR,CAAC,EAAE,EAAE7B,UAAU,QAAQ,QAAQ,uFAAuF,CAAC;IAE3H;IACAa,OAAOgB,GAAG,CAAC;IACXhB,OAAOgB,GAAG,CACR7B,UAAU;QAAC;QAAQ;KAAY,EAAE;IAEnC,IAAIU,iBAAiBA,cAAcsB,MAAM,GAAG,GAAG;QAC7C,MAAMC,UAAU,MAAM7B,qBAAqBM;QAC3CG,OAAOgB,GAAG,CAAC,CAAC,EAAE,EAAEI,SAAS;QACzBpB,OAAOgB,GAAG,CAAC,CAAC,cAAc,EAAE7B,UAAU,QAAQ,0BAA0B;QACxEa,OAAOgB,GAAG,CACR,CAAC,2CAA2C,EAAE7B,UAAU,QAAQ,yCAAyC;IAE7G;IACAa,OAAOgB,GAAG,CAAC;IACXhB,OAAOgB,GAAG,CAAC,CAAC,uBAAuB,CAAC;IACpChB,OAAOgB,GAAG,CAAC,CAAC,iEAAiE,CAAC;IAC9EhB,OAAOgB,GAAG,CAAC,CAAC,uEAAuE,CAAC;IACpFhB,OAAOgB,GAAG,CAAC,CAAC,6CAA6C,CAAC;AAC5D"}
1
+ {"version":3,"sources":["../../../src/actions/init/initApp.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type Output, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {isStaging} from '@sanity/cli-core/util'\nimport {logSymbols} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\n\nimport {getMediaLibrariesForOrganization, type MediaLibrary} from '../../services/mediaLibraries.js'\nimport {type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {getInstallCommand} from '../../util/packageManager/installPackages.js'\nimport {type EditorName} from '../mcp/editorConfigs.js'\nimport {determineMediaLibraryTemplate} from './determineMediaLibraryTemplate.js'\nimport {InitError} from './initError.js'\nimport {getPostInitMCPPrompt} from './initHelpers.js'\nimport {offerAspectPull} from './offerAspectPull.js'\nimport {type RepoInfo} from './remoteTemplate.js'\nimport {scaffoldAndInstall, selectTemplate} from './scaffoldTemplate.js'\nimport {type InitOptions} from './types.js'\n\nexport async function initApp({\n datasetName,\n defaults,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workbench,\n workDir,\n}: {\n datasetName: string\n defaults: {projectName: string}\n mcpConfigured: EditorName[]\n options: InitOptions\n organizationId: string | undefined\n output: Output\n outputPath: string\n projectId: string\n remoteTemplateInfo: RepoInfo | undefined\n sluggedName: string\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>\n workbench: boolean\n workDir: string\n}): Promise<void> {\n const {\n template: resolvedTemplate,\n templateName,\n useTypeScript,\n } = await selectTemplate({\n options,\n remoteTemplateInfo,\n trace,\n })\n\n if (!remoteTemplateInfo && !resolvedTemplate) {\n throw new InitError(`Template \"${templateName}\" not found`, 1)\n }\n\n const {pkgManager} = await scaffoldAndInstall({\n datasetName,\n defaults,\n displayName: '',\n options,\n organizationId,\n output,\n outputPath,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n templateName,\n trace,\n useTypeScript,\n workbench,\n workDir,\n })\n\n const isCurrentDir = outputPath === workDir\n const goToProjectDir = `\\n(${styleText('cyan', `cd ${outputPath}`)} to navigate to your new project directory)`\n\n if (determineMediaLibraryTemplate(templateName)) {\n output.log(\n `${logSymbols.success} ${styleText(['green', 'bold'], 'Success!')} Your Media Library config app has been scaffolded.`,\n )\n if (!isCurrentDir) output.log(goToProjectDir)\n if (!options.install) {\n output.log(\n `\\nRun ${styleText('cyan', getInstallCommand(pkgManager))} to install dependencies.`,\n )\n }\n let libraries: MediaLibrary[] = []\n if (organizationId) {\n output.log(`\\nConfigured for organization ${styleText('cyan', organizationId)}.`)\n libraries = await warnIfNoActiveMediaLibrary(organizationId, output)\n }\n\n // The deploy mirrors config into the library and deletes stored aspects\n // the config does not declare, so an org that already defines aspects is\n // offered a pull now — while init is interactive and authenticated. Only\n // with exactly one active library; picking one is pull-aspects territory.\n let pulledAspects = false\n if (libraries.length === 1 && !options.unattended) {\n output.log('')\n pulledAspects = await offerAspectPull({library: libraries[0], output, outputPath})\n }\n\n output.log(\n '\\nField definitions live in `src/fields/` and are registered in `sanity.cli.ts` — see the README for the authoring rules.',\n )\n if (libraries.length > 0 && !pulledAspects) {\n output.log(\n `\\nAlready defining aspects in your Media Library? Generate field modules from them with ${styleText('cyan', 'npx sanity media pull-aspects')}.`,\n )\n }\n output.log('\\nRefer to our documentation for a walkthrough:')\n output.log(styleText(['blue', 'underline'], 'https://www.sanity.io/docs/media-library'))\n await logMcpConfirmation(mcpConfigured, output)\n output.log('\\n')\n output.log(`Other helpful commands:`)\n output.log(`npx sanity docs browse to open the documentation in a browser`)\n output.log(`npx sanity dev to serve your fields to the Media Library live`)\n output.log(`npx sanity deploy to deploy the config to your organization`)\n return\n }\n\n //output for custom apps here\n output.log(\n `${logSymbols.success} ${styleText(['green', 'bold'], 'Success!')} Your custom app has been scaffolded.`,\n )\n if (!isCurrentDir) output.log(goToProjectDir)\n if (!options.install) {\n output.log(`\\nRun ${styleText('cyan', getInstallCommand(pkgManager))} to install dependencies.`)\n }\n\n if (projectId && datasetName) {\n output.log(\n `\\nConfigured with project ${styleText('cyan', projectId)} and dataset ${styleText('cyan', datasetName)}.`,\n )\n output.log(\n 'Edit `src/App.tsx` to change these values or add more project / dataset pairs to your config.',\n )\n } else {\n output.log(\n `\\n${styleText('bold', 'Next')}, configure the project(s) and dataset(s) your app should work with in \\`src/App.tsx\\`.`,\n )\n }\n output.log('\\nRefer to our documentation for a walkthrough:')\n output.log(\n styleText(['blue', 'underline'], 'https://www.sanity.io/docs/app-sdk/sdk-configuration'),\n )\n await logMcpConfirmation(mcpConfigured, output)\n output.log('\\n')\n output.log(`Other helpful commands:`)\n output.log(`npx sanity docs browse to open the documentation in a browser`)\n output.log(`npx sanity dev to start the development server for your app`)\n output.log(`npx sanity deploy to deploy your app`)\n}\n\n/**\n * The post-init MCP confirmation shown by every template branch: which\n * editors were configured during init and where to learn more. No-op when\n * MCP setup was skipped or configured nothing.\n */\nasync function logMcpConfirmation(mcpConfigured: EditorName[], output: Output): Promise<void> {\n if (!mcpConfigured || mcpConfigured.length === 0) return\n const message = await getPostInitMCPPrompt(mcpConfigured)\n output.log(`\\n${message}`)\n output.log(`\\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`)\n output.log(\n `\\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`,\n )\n}\n\n/**\n * Media Library is provisioned per organization, outside this app (in\n * Manage). Without an active library the dashboard's media area comes up\n * empty, so say so now — init is the one interactive, authenticated moment.\n * API errors (offline, no access to the org, timeout) are swallowed: this is\n * a hint, not a gate.\n *\n * @returns the organization's active libraries — empty also on lookup\n * failure, so follow-up suggestions stay silent in the unknown case.\n */\nexport async function warnIfNoActiveMediaLibrary(\n organizationId: string,\n output: Output,\n): Promise<MediaLibrary[]> {\n try {\n const libraries = await getMediaLibrariesForOrganization(organizationId)\n if (libraries.length > 0) return libraries\n } catch {\n return []\n }\n\n const manageUrl = `${isStaging() ? 'https://sanity.work' : 'https://www.sanity.io'}/manage`\n // No symbol or leading newline: output.warn already prefixes `Warning:`.\n output.warn(\n `Organization ${organizationId} has no active Media Library, ` +\n `so the dashboard's media area will be empty. Media Library is provisioned per ` +\n `organization — enable it in Manage (${manageUrl}) or learn more at ` +\n `https://www.sanity.io/docs/media-library.`,\n )\n return []\n}\n"],"names":["styleText","isStaging","logSymbols","getMediaLibrariesForOrganization","getInstallCommand","determineMediaLibraryTemplate","InitError","getPostInitMCPPrompt","offerAspectPull","scaffoldAndInstall","selectTemplate","initApp","datasetName","defaults","mcpConfigured","options","organizationId","output","outputPath","projectId","remoteTemplateInfo","sluggedName","trace","workbench","workDir","template","resolvedTemplate","templateName","useTypeScript","pkgManager","displayName","isCurrentDir","goToProjectDir","log","success","install","libraries","warnIfNoActiveMediaLibrary","pulledAspects","length","unattended","library","logMcpConfirmation","message","manageUrl","warn"],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { confirm, logSymbols, spinner } from '@sanity/cli-core/ux';
4
+ import { getAspects } from '../../services/mediaLibraries.js';
5
+ import { pluralize } from '../../util/pluralize.js';
6
+ import { replaceFieldEntries } from '../media/cliConfigFields.js';
7
+ import { convertAspectDocuments, formatFieldEntries } from '../media/pullAspects.js';
8
+ /** Where the scaffold keeps field modules — the template's `fields[*].src` dir. */ const FIELDS_DIR = path.join('src', 'fields');
9
+ /**
10
+ * Offer to seed the freshly scaffolded config app from the organization's
11
+ * existing aspects. Deploying the config mirrors it into the library —
12
+ * stored aspects the config does not declare get deleted — so a scaffold
13
+ * that starts from the example fields instead of the real ones is a loaded
14
+ * footgun. Pulling at init time defuses it while we are still interactive
15
+ * and authenticated.
16
+ *
17
+ * Only offered when the organization has exactly one active library (no
18
+ * prompt-stacking during init); lookups and conversion errors degrade to a
19
+ * silent skip — the scaffold is complete without this, and
20
+ * `sanity media pull-aspects` covers it later.
21
+ *
22
+ * @returns `true` when the example fields were replaced by pulled modules.
23
+ */ export async function offerAspectPull({ library, output, outputPath }) {
24
+ const spin = spinner('Checking the media library for existing aspects').start();
25
+ let documents;
26
+ try {
27
+ documents = await getAspects(library.id);
28
+ } catch {
29
+ return false;
30
+ } finally{
31
+ spin.stop();
32
+ }
33
+ if (documents.length === 0) return false;
34
+ const { modules, skipped } = convertAspectDocuments(documents);
35
+ if (modules.length === 0) return false;
36
+ const shouldPull = await confirm({
37
+ default: true,
38
+ message: `Media library ${library.id} already defines ${modules.length} ${pluralize('aspect', modules.length)}. ` + 'Generate field modules from them (replaces the example fields)?'
39
+ });
40
+ if (!shouldPull) return false;
41
+ if (skipped.length > 0) {
42
+ for (const aspect of skipped){
43
+ const errors = aspect.validationErrors.flatMap((group)=>group.map(({ message })=>message));
44
+ output.warn(`Skipped aspect ${aspect.id}: ${[
45
+ ...aspect.reasons,
46
+ ...errors
47
+ ].join('; ')}`);
48
+ }
49
+ }
50
+ // The config edit is resolved before anything touches the disk: bailing
51
+ // after deleting the example modules would leave the scaffold declaring
52
+ // files that no longer exist. On bail the scaffold stays a consistent,
53
+ // working pair (example fields + their declarations), and the caller's
54
+ // `!pulledAspects` branch already points at `sanity media pull-aspects`.
55
+ // The template is `typescriptOnly`, so the scaffold always writes this file.
56
+ const configPath = path.join(outputPath, 'sanity.cli.ts');
57
+ const entryLines = formatFieldEntries(modules, FIELDS_DIR.replaceAll(path.sep, '/')).split('\n');
58
+ const result = replaceFieldEntries(await readFile(configPath, 'utf8'), entryLines);
59
+ if (!result.ok) {
60
+ output.warn(`Could not seed the config from the existing aspects (${result.reason}) — the example fields are kept.`);
61
+ return false;
62
+ }
63
+ // The example modules make way for the real ones: entries are replaced
64
+ // wholesale in the config, so files left behind would just be orphans.
65
+ const fieldsDir = path.join(outputPath, FIELDS_DIR);
66
+ await rm(fieldsDir, {
67
+ force: true,
68
+ recursive: true
69
+ });
70
+ await mkdir(fieldsDir, {
71
+ recursive: true
72
+ });
73
+ for (const module of modules){
74
+ await writeFile(path.join(fieldsDir, module.filename), module.source);
75
+ }
76
+ await writeFile(configPath, result.updated);
77
+ output.log(`${logSymbols.success} Generated ${modules.length} field ${pluralize('module', modules.length)} from your media library and declared them in sanity.cli.ts`);
78
+ return true;
79
+ }
80
+
81
+ //# sourceMappingURL=offerAspectPull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/init/offerAspectPull.ts"],"sourcesContent":["import {mkdir, readFile, rm, writeFile} from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {type Output} from '@sanity/cli-core'\nimport {confirm, logSymbols, spinner} from '@sanity/cli-core/ux'\n\nimport {getAspects, type MediaLibrary} from '../../services/mediaLibraries.js'\nimport {pluralize} from '../../util/pluralize.js'\nimport {replaceFieldEntries} from '../media/cliConfigFields.js'\nimport {\n convertAspectDocuments,\n formatFieldEntries,\n type PullableAspectDocument,\n} from '../media/pullAspects.js'\n\n/** Where the scaffold keeps field modules — the template's `fields[*].src` dir. */\nconst FIELDS_DIR = path.join('src', 'fields')\n\n/**\n * Offer to seed the freshly scaffolded config app from the organization's\n * existing aspects. Deploying the config mirrors it into the library —\n * stored aspects the config does not declare get deleted — so a scaffold\n * that starts from the example fields instead of the real ones is a loaded\n * footgun. Pulling at init time defuses it while we are still interactive\n * and authenticated.\n *\n * Only offered when the organization has exactly one active library (no\n * prompt-stacking during init); lookups and conversion errors degrade to a\n * silent skip — the scaffold is complete without this, and\n * `sanity media pull-aspects` covers it later.\n *\n * @returns `true` when the example fields were replaced by pulled modules.\n */\nexport async function offerAspectPull({\n library,\n output,\n outputPath,\n}: {\n library: MediaLibrary\n output: Output\n outputPath: string\n}): Promise<boolean> {\n const spin = spinner('Checking the media library for existing aspects').start()\n let documents: PullableAspectDocument[]\n try {\n documents = await getAspects(library.id)\n } catch {\n return false\n } finally {\n spin.stop()\n }\n if (documents.length === 0) return false\n\n const {modules, skipped} = convertAspectDocuments(documents)\n if (modules.length === 0) return false\n\n const shouldPull = await confirm({\n default: true,\n message:\n `Media library ${library.id} already defines ${modules.length} ${pluralize('aspect', modules.length)}. ` +\n 'Generate field modules from them (replaces the example fields)?',\n })\n if (!shouldPull) return false\n\n if (skipped.length > 0) {\n for (const aspect of skipped) {\n const errors = aspect.validationErrors.flatMap((group) => group.map(({message}) => message))\n output.warn(`Skipped aspect ${aspect.id}: ${[...aspect.reasons, ...errors].join('; ')}`)\n }\n }\n\n // The config edit is resolved before anything touches the disk: bailing\n // after deleting the example modules would leave the scaffold declaring\n // files that no longer exist. On bail the scaffold stays a consistent,\n // working pair (example fields + their declarations), and the caller's\n // `!pulledAspects` branch already points at `sanity media pull-aspects`.\n // The template is `typescriptOnly`, so the scaffold always writes this file.\n const configPath = path.join(outputPath, 'sanity.cli.ts')\n const entryLines = formatFieldEntries(modules, FIELDS_DIR.replaceAll(path.sep, '/')).split('\\n')\n\n const result = replaceFieldEntries(await readFile(configPath, 'utf8'), entryLines)\n if (!result.ok) {\n output.warn(\n `Could not seed the config from the existing aspects (${result.reason}) — the example fields are kept.`,\n )\n return false\n }\n\n // The example modules make way for the real ones: entries are replaced\n // wholesale in the config, so files left behind would just be orphans.\n const fieldsDir = path.join(outputPath, FIELDS_DIR)\n await rm(fieldsDir, {force: true, recursive: true})\n await mkdir(fieldsDir, {recursive: true})\n for (const module of modules) {\n await writeFile(path.join(fieldsDir, module.filename), module.source)\n }\n\n await writeFile(configPath, result.updated)\n output.log(\n `${logSymbols.success} Generated ${modules.length} field ${pluralize('module', modules.length)} from your media library and declared them in sanity.cli.ts`,\n )\n return true\n}\n"],"names":["mkdir","readFile","rm","writeFile","path","confirm","logSymbols","spinner","getAspects","pluralize","replaceFieldEntries","convertAspectDocuments","formatFieldEntries","FIELDS_DIR","join","offerAspectPull","library","output","outputPath","spin","start","documents","id","stop","length","modules","skipped","shouldPull","default","message","aspect","errors","validationErrors","flatMap","group","map","warn","reasons","configPath","entryLines","replaceAll","sep","split","result","ok","reason","fieldsDir","force","recursive","module","filename","source","updated","log","success"],"mappings":"AAAA,SAAQA,KAAK,EAAEC,QAAQ,EAAEC,EAAE,EAAEC,SAAS,QAAO,mBAAkB;AAC/D,OAAOC,UAAU,YAAW;AAG5B,SAAQC,OAAO,EAAEC,UAAU,EAAEC,OAAO,QAAO,sBAAqB;AAEhE,SAAQC,UAAU,QAA0B,mCAAkC;AAC9E,SAAQC,SAAS,QAAO,0BAAyB;AACjD,SAAQC,mBAAmB,QAAO,8BAA6B;AAC/D,SACEC,sBAAsB,EACtBC,kBAAkB,QAEb,0BAAyB;AAEhC,iFAAiF,GACjF,MAAMC,aAAaT,KAAKU,IAAI,CAAC,OAAO;AAEpC;;;;;;;;;;;;;;CAcC,GACD,OAAO,eAAeC,gBAAgB,EACpCC,OAAO,EACPC,MAAM,EACNC,UAAU,EAKX;IACC,MAAMC,OAAOZ,QAAQ,mDAAmDa,KAAK;IAC7E,IAAIC;IACJ,IAAI;QACFA,YAAY,MAAMb,WAAWQ,QAAQM,EAAE;IACzC,EAAE,OAAM;QACN,OAAO;IACT,SAAU;QACRH,KAAKI,IAAI;IACX;IACA,IAAIF,UAAUG,MAAM,KAAK,GAAG,OAAO;IAEnC,MAAM,EAACC,OAAO,EAAEC,OAAO,EAAC,GAAGf,uBAAuBU;IAClD,IAAII,QAAQD,MAAM,KAAK,GAAG,OAAO;IAEjC,MAAMG,aAAa,MAAMtB,QAAQ;QAC/BuB,SAAS;QACTC,SACE,CAAC,cAAc,EAAEb,QAAQM,EAAE,CAAC,iBAAiB,EAAEG,QAAQD,MAAM,CAAC,CAAC,EAAEf,UAAU,UAAUgB,QAAQD,MAAM,EAAE,EAAE,CAAC,GACxG;IACJ;IACA,IAAI,CAACG,YAAY,OAAO;IAExB,IAAID,QAAQF,MAAM,GAAG,GAAG;QACtB,KAAK,MAAMM,UAAUJ,QAAS;YAC5B,MAAMK,SAASD,OAAOE,gBAAgB,CAACC,OAAO,CAAC,CAACC,QAAUA,MAAMC,GAAG,CAAC,CAAC,EAACN,OAAO,EAAC,GAAKA;YACnFZ,OAAOmB,IAAI,CAAC,CAAC,eAAe,EAAEN,OAAOR,EAAE,CAAC,EAAE,EAAE;mBAAIQ,OAAOO,OAAO;mBAAKN;aAAO,CAACjB,IAAI,CAAC,OAAO;QACzF;IACF;IAEA,wEAAwE;IACxE,wEAAwE;IACxE,uEAAuE;IACvE,uEAAuE;IACvE,yEAAyE;IACzE,6EAA6E;IAC7E,MAAMwB,aAAalC,KAAKU,IAAI,CAACI,YAAY;IACzC,MAAMqB,aAAa3B,mBAAmBa,SAASZ,WAAW2B,UAAU,CAACpC,KAAKqC,GAAG,EAAE,MAAMC,KAAK,CAAC;IAE3F,MAAMC,SAASjC,oBAAoB,MAAMT,SAASqC,YAAY,SAASC;IACvE,IAAI,CAACI,OAAOC,EAAE,EAAE;QACd3B,OAAOmB,IAAI,CACT,CAAC,qDAAqD,EAAEO,OAAOE,MAAM,CAAC,gCAAgC,CAAC;QAEzG,OAAO;IACT;IAEA,uEAAuE;IACvE,uEAAuE;IACvE,MAAMC,YAAY1C,KAAKU,IAAI,CAACI,YAAYL;IACxC,MAAMX,GAAG4C,WAAW;QAACC,OAAO;QAAMC,WAAW;IAAI;IACjD,MAAMhD,MAAM8C,WAAW;QAACE,WAAW;IAAI;IACvC,KAAK,MAAMC,UAAUxB,QAAS;QAC5B,MAAMtB,UAAUC,KAAKU,IAAI,CAACgC,WAAWG,OAAOC,QAAQ,GAAGD,OAAOE,MAAM;IACtE;IAEA,MAAMhD,UAAUmC,YAAYK,OAAOS,OAAO;IAC1CnC,OAAOoC,GAAG,CACR,GAAG/C,WAAWgD,OAAO,CAAC,WAAW,EAAE7B,QAAQD,MAAM,CAAC,OAAO,EAAEf,UAAU,UAAUgB,QAAQD,MAAM,EAAE,2DAA2D,CAAC;IAE7J,OAAO;AACT"}
@@ -1,9 +1,10 @@
1
1
  import { subdebug } from '@sanity/cli-core';
2
- import { select, Separator } from '@sanity/cli-core/ux';
2
+ import { search, Separator } from '@sanity/cli-core/ux';
3
3
  import { promptForDatasetName } from '../../../prompts/promptForDatasetName.js';
4
4
  import { promptForDefaultConfig } from '../../../prompts/promptForDefaultConfig.js';
5
5
  import { listDatasets } from '../../../services/datasets.js';
6
6
  import { getProjectFeatures } from '../../../services/getProjectFeatures.js';
7
+ import { searchChoices, searchTheme } from '../../../util/searchChoices.js';
7
8
  import { createDataset } from '../../dataset/create.js';
8
9
  const debug = subdebug('init');
9
10
  export async function getOrCreateDataset(opts) {
@@ -81,18 +82,20 @@ export async function getOrCreateDataset(opts) {
81
82
  }
82
83
  debug(`User has ${datasets.length} dataset(s) already, showing list of choices`);
83
84
  const datasetChoices = datasets.map((dataset)=>({
85
+ name: dataset.name,
84
86
  value: dataset.name
85
87
  }));
86
- const selected = await select({
87
- choices: [
88
+ const selected = await search({
89
+ message: 'Select dataset to use',
90
+ source: searchChoices([
88
91
  {
89
92
  name: 'Create new dataset',
90
93
  value: 'new'
91
94
  },
92
95
  new Separator(),
93
96
  ...datasetChoices
94
- ],
95
- message: 'Select dataset to use'
97
+ ]),
98
+ theme: searchTheme
96
99
  });
97
100
  if (selected === 'new') {
98
101
  const existingDatasetNames = datasets.map((ds)=>ds.name);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/init/project/getOrCreateDataset.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\nimport {select, 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 {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 if (dataset && opts.unattended) {\n return {datasetName: dataset, userAction: 'none'}\n }\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 output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n }\n\n return {datasetName: dataset, userAction: 'none'}\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) => ({value: dataset.name}))\n\n const selected = await select({\n choices: [{name: 'Create new dataset', value: 'new'}, new Separator(), ...datasetChoices],\n message: 'Select dataset to use',\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","select","Separator","promptForDatasetName","promptForDefaultConfig","listDatasets","getProjectFeatures","createDataset","debug","getOrCreateDataset","opts","dataset","visibility","defaultConfig","unattended","datasetName","userAction","datasets","projectFeatures","Promise","all","projectId","existing","find","ds","name","forcePublic","output","undefined","isUnattended","length","showDefaultConfigPrompt","message","datasetChoices","map","value","selected","choices","existingDatasetNames","includes","newDatasetName"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AACzC,SAAQC,MAAM,EAAEC,SAAS,QAAO,sBAAqB;AAErD,SAAQC,oBAAoB,QAAO,2CAA0C;AAC7E,SAAQC,sBAAsB,QAAO,6CAA4C;AACjF,SAAQC,YAAY,QAAO,gCAA+B;AAC1D,SAAQC,kBAAkB,QAAO,0CAAyC;AAC1E,SAAQC,aAAa,QAAO,0BAAyB;AAGrD,MAAMC,QAAQR,SAAS;AAEvB,OAAO,eAAeS,mBAAmBC,IASxC;IAIC,MAAM,EAACC,OAAO,EAAEC,UAAU,EAAC,GAAGF;IAC9B,IAAI,EAACG,aAAa,EAAC,GAAGH;IAEtB,IAAIC,WAAWD,KAAKI,UAAU,EAAE;QAC9B,OAAO;YAACC,aAAaJ;YAASK,YAAY;QAAM;IAClD;IAEA,MAAM,CAACC,UAAUC,gBAAgB,GAAG,MAAMC,QAAQC,GAAG,CAAC;QACpDf,aAAaK,KAAKW,SAAS;QAC3Bf,mBAAmBI,KAAKW,SAAS;KAClC;IAED,IAAIV,SAAS;QACXH,MAAM,kDAAkDG;QACxD,MAAMW,WAAWL,SAASM,IAAI,CAAC,CAACC,KAAOA,GAAGC,IAAI,KAAKd;QACnD,IAAI,CAACW,UAAU;YACbd,MAAM;YACN,MAAMD,cAAc;gBAClBQ,aAAaJ;gBACbe,aAAab;gBACbc,QAAQjB,KAAKiB,MAAM;gBACnBT;gBACAG,WAAWX,KAAKW,SAAS;gBACzBT;YACF;QACF;QAEA,OAAO;YAACG,aAAaJ;YAASK,YAAY;QAAM;IAClD;IAEA,IAAIN,KAAKI,UAAU,EAAE;QACnBN,MAAM;QACN,MAAMO,cAAc;QACpB,MAAMO,WAAWL,SAASM,IAAI,CAAC,CAACC,KAAOA,GAAGC,IAAI,KAAKV;QACnD,IAAI,CAACO,UAAU;YACb,MAAMf,cAAc;gBAClBQ;gBACAW,aAAad,eAAegB;gBAC5BC,cAAc;gBACdF,QAAQjB,KAAKiB,MAAM;gBACnBT;gBACAG,WAAWX,KAAKW,SAAS;gBACzBT;YACF;QACF;QACA,OAAO;YAACG;YAAaC,YAAYM,WAAW,SAAS;QAAQ;IAC/D;IAEA,IAAIL,SAASa,MAAM,KAAK,GAAG;QACzBtB,MAAM;QACN,IAAIE,KAAKqB,uBAAuB,EAAE;YAChClB,gBAAgB,MAAMT;QACxB;QACA,MAAMqB,OAAOZ,gBACT,eACA,MAAMV,qBAAqB;YACzB6B,SAAS;QACX;QACJ,MAAMzB,cAAc;YAClBQ,aAAaU;YACbC,aAAab;YACbc,QAAQjB,KAAKiB,MAAM;YACnBT;YACAG,WAAWX,KAAKW,SAAS;YACzBT;QACF;QACA,OAAO;YAACG,aAAaU;YAAMT,YAAY;QAAQ;IACjD;IAEAR,MAAM,CAAC,SAAS,EAAES,SAASa,MAAM,CAAC,4CAA4C,CAAC;IAC/E,MAAMG,iBAAiBhB,SAASiB,GAAG,CAAC,CAACvB,UAAa,CAAA;YAACwB,OAAOxB,QAAQc,IAAI;QAAA,CAAA;IAEtE,MAAMW,WAAW,MAAMnC,OAAO;QAC5BoC,SAAS;YAAC;gBAACZ,MAAM;gBAAsBU,OAAO;YAAK;YAAG,IAAIjC;eAAgB+B;SAAe;QACzFD,SAAS;IACX;IAEA,IAAII,aAAa,OAAO;QACtB,MAAME,uBAAuBrB,SAASiB,GAAG,CAAC,CAACV,KAAOA,GAAGC,IAAI;QACzDjB,MAAM;QACN,IAAIE,KAAKqB,uBAAuB,IAAI,CAACO,qBAAqBC,QAAQ,CAAC,eAAe;YAChF1B,gBAAgB,MAAMT;QACxB;QAEA,MAAMoC,iBAAiB3B,gBACnB,eACA,MAAMV,qBACJ;YACE6B,SAAS;QACX,GACAM;QAEN,MAAM/B,cAAc;YAClBQ,aAAayB;YACbd,aAAab;YACbc,QAAQjB,KAAKiB,MAAM;YACnBT;YACAG,WAAWX,KAAKW,SAAS;YACzBT;QACF;QACA,OAAO;YAACG,aAAayB;YAAgBxB,YAAY;QAAQ;IAC3D;IAEAR,MAAM,CAAC,4BAA4B,EAAE4B,SAAS,CAAC,CAAC;IAChD,OAAO;QAACrB,aAAaqB;QAAUpB,YAAY;IAAQ;AACrD"}
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 if (dataset && opts.unattended) {\n return {datasetName: dataset, userAction: 'none'}\n }\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 output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n }\n\n return {datasetName: dataset, userAction: 'none'}\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","unattended","datasetName","userAction","datasets","projectFeatures","Promise","all","projectId","existing","find","ds","name","forcePublic","output","undefined","isUnattended","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 { select, Separator } from '@sanity/cli-core/ux';
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 select({
91
- choices: [
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
- message: 'Create a new project or select an existing one'
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 {select, Separator} from '@sanity/cli-core/ux'\n\nimport {listOrganizations, type ProjectOrganization} from '../../../services/organizations.js'\nimport {listProjects} from '../../../services/projects.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 select({\n choices: [{name: 'Create new project', value: 'new'}, new Separator(), ...projectChoices],\n message: 'Create a new project or select an existing one',\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","select","Separator","listOrganizations","listProjects","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","choices","selectedProject"],"mappings":"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"}
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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"}
@@ -6,11 +6,13 @@ import { getOrCreateProject } from './getOrCreateProject.js';
6
6
  import { promptForAppTemplateSetup } from './promptForAppTemplateSetup.js';
7
7
  import { promptUserForOrganization } from './promptUserForOrganization.js';
8
8
  const debug = subdebug('init');
9
- export async function getProjectDetails({ coupon, dataset, datasetDefault, isAppTemplate, newProject, organization, output, planId, project, showDefaultConfigPrompt, trace, unattended, user, visibility }) {
9
+ export async function getProjectDetails({ coupon, dataset, datasetDefault, isAppTemplate, isMediaLibraryTemplate = false, newProject, organization, output, planId, project, showDefaultConfigPrompt, trace, unattended, user, visibility }) {
10
10
  if (isAppTemplate) {
11
11
  const hasProjectFlag = Boolean(project || newProject);
12
12
  let appOrganizationId = organization;
13
- if (!appOrganizationId && !hasProjectFlag) {
13
+ // A media-library config is organization-only, so the org is always
14
+ // resolved here — project flags can't stand in for it.
15
+ if (!appOrganizationId && (isMediaLibraryTemplate || !hasProjectFlag)) {
14
16
  let organizations;
15
17
  try {
16
18
  organizations = await listOrganizations();
@@ -24,6 +26,27 @@ export async function getProjectDetails({ coupon, dataset, datasetDefault, isApp
24
26
  user
25
27
  });
26
28
  }
29
+ // The media-library config scaffold records only `app.organizationId`, so
30
+ // the project/dataset setup below would create or select resources the
31
+ // template throws away — and picking a project from another organization
32
+ // would silently override the chosen org. Skip it entirely.
33
+ if (isMediaLibraryTemplate) {
34
+ if (hasProjectFlag) {
35
+ output.warn('Project flags are ignored for the media-library-config template — it only records the organization.');
36
+ }
37
+ trace.log({
38
+ datasetName: '',
39
+ selectedOption: 'skip',
40
+ step: 'createOrSelectDataset'
41
+ });
42
+ return {
43
+ datasetName: '',
44
+ displayName: '',
45
+ isFirstProject: false,
46
+ organizationId: appOrganizationId,
47
+ projectId: ''
48
+ };
49
+ }
27
50
  const { datasetName: appDatasetName, displayName: appDisplayName, organizationId: resolvedOrganizationId, projectId: appProjectId } = await promptForAppTemplateSetup({
28
51
  coupon,
29
52
  dataset,