@sanity/cli 8.23.0 → 8.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +401 -87
- package/bin/run.js +7 -1
- package/dist/actions/codemods/dashboard.js +136 -8
- package/dist/actions/codemods/dashboard.js.map +1 -1
- package/dist/actions/codemods/types.js.map +1 -1
- package/dist/actions/dev/devAction.js +48 -44
- package/dist/actions/dev/devAction.js.map +1 -1
- package/dist/actions/init/initHelpers.js +1 -1
- package/dist/actions/init/initHelpers.js.map +1 -1
- package/dist/actions/init/initStudio.js +1 -1
- package/dist/actions/init/initStudio.js.map +1 -1
- package/dist/actions/init/project/getOrCreateDataset.js +10 -12
- package/dist/actions/init/project/getOrCreateDataset.js.map +1 -1
- package/dist/actions/init/project/getOrCreateProject.js +7 -5
- package/dist/actions/init/project/getOrCreateProject.js.map +1 -1
- package/dist/actions/init/project/promptUserForOrganization.js +6 -4
- package/dist/actions/init/project/promptUserForOrganization.js.map +1 -1
- package/dist/commands/api.js +1 -1
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/assets/upload.js +1 -1
- package/dist/commands/assets/upload.js.map +1 -1
- package/dist/commands/codemod.js +1 -0
- package/dist/commands/codemod.js.map +1 -1
- package/dist/commands/cors/add.js +1 -1
- package/dist/commands/cors/add.js.map +1 -1
- package/dist/commands/cors/delete.js +1 -1
- package/dist/commands/cors/delete.js.map +1 -1
- package/dist/commands/cors/list.js +1 -1
- package/dist/commands/cors/list.js.map +1 -1
- package/dist/commands/datasets/alias/create.js +1 -1
- package/dist/commands/datasets/alias/create.js.map +1 -1
- package/dist/commands/datasets/import.js +0 -8
- package/dist/commands/datasets/import.js.map +1 -1
- package/dist/commands/datasets/list.js +1 -1
- package/dist/commands/datasets/list.js.map +1 -1
- package/dist/commands/documents/create.js +1 -1
- package/dist/commands/documents/create.js.map +1 -1
- package/dist/commands/documents/delete.js +1 -1
- package/dist/commands/documents/delete.js.map +1 -1
- package/dist/commands/documents/get.js +1 -1
- package/dist/commands/documents/get.js.map +1 -1
- package/dist/commands/documents/query.js +1 -9
- package/dist/commands/documents/query.js.map +1 -1
- package/dist/commands/documents/validate.js +2 -2
- package/dist/commands/documents/validate.js.map +1 -1
- package/dist/commands/graphql/list.js +1 -1
- package/dist/commands/graphql/list.js.map +1 -1
- package/dist/commands/graphql/undeploy.js +1 -11
- package/dist/commands/graphql/undeploy.js.map +1 -1
- package/dist/commands/hooks/attempt.js +1 -1
- package/dist/commands/hooks/attempt.js.map +1 -1
- package/dist/commands/hooks/create.js +1 -1
- package/dist/commands/hooks/create.js.map +1 -1
- package/dist/commands/hooks/delete.js +1 -1
- package/dist/commands/hooks/delete.js.map +1 -1
- package/dist/commands/hooks/list.js +1 -1
- package/dist/commands/hooks/list.js.map +1 -1
- package/dist/commands/hooks/logs.js +1 -1
- package/dist/commands/hooks/logs.js.map +1 -1
- package/dist/commands/init.js +3 -5
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrations/run.js +5 -5
- package/dist/commands/migrations/run.js.map +1 -1
- package/dist/commands/projects/unclaimed.js +6 -5
- package/dist/commands/projects/unclaimed.js.map +1 -1
- package/dist/commands/tokens/create.js +4 -4
- package/dist/commands/tokens/create.js.map +1 -1
- package/dist/commands/tokens/delete.js +2 -2
- package/dist/commands/tokens/delete.js.map +1 -1
- package/dist/commands/tokens/list.js +2 -2
- package/dist/commands/tokens/list.js.map +1 -1
- package/dist/commands/users/invite.js +1 -1
- package/dist/commands/users/invite.js.map +1 -1
- package/dist/commands/users/list.js +1 -1
- package/dist/commands/users/list.js.map +1 -1
- package/dist/exports/invokeSanityCli/index.js +1 -1
- package/dist/exports/invokeSanityCli/index.js.map +1 -1
- package/dist/hooks/prerun/injectEnvVariables.js +1 -1
- package/dist/hooks/prerun/injectEnvVariables.js.map +1 -1
- package/dist/util/searchChoices.js +52 -0
- package/dist/util/searchChoices.js.map +1 -0
- package/dist/util/sharedFlags.js +8 -4
- package/dist/util/sharedFlags.js.map +1 -1
- package/dist/util/warnOnNonProductionEnvironment.js +4 -3
- package/dist/util/warnOnNonProductionEnvironment.js.map +1 -1
- package/oclif.manifest.json +515 -351
- package/package.json +8 -8
- package/dist/util/getSanityEnv.js +0 -8
- package/dist/util/getSanityEnv.js.map +0 -1
- package/templates/shopify/.gitignore +0 -26
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/migrations/run.ts"],"sourcesContent":["import path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, getProjectCliClient, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\nimport {\n type APIConfig,\n DEFAULT_MUTATION_CONCURRENCY,\n dryRun,\n MAX_MUTATION_CONCURRENCY,\n type Migration,\n type MigrationProgress,\n run,\n UnknownTransactionOutcomeError,\n} from '@sanity/migrate'\n\nimport {DEFAULT_API_VERSION, MIGRATIONS_DIRECTORY} from '../../actions/migration/constants.js'\nimport {ensureApiVersionFormat} from '../../actions/migration/ensureApiVersionFormat.js'\nimport {prettyFormat} from '../../actions/migration/prettyMutationFormatter.js'\nimport {resolveMigrations} from '../../actions/migration/resolveMigrations.js'\nimport {\n isLoadableMigrationScript,\n resolveMigrationScript,\n} from '../../actions/migration/resolveMigrationScript.js'\nimport {Table} from '../../util/responsiveTable.js'\n\nconst runMigrationDebug = subdebug('migration:run')\n\n/**\n * Renders the `cause` chain of an error the way oclif's own error printer would.\n *\n * Used when printing an error ourselves to bypass oclif's hard word wrapping:\n * the underlying transport error (a timeout, a dropped connection) is the part\n * that explains _why_ the outcome is unknown, so it should not be dropped just\n * because the top-level message is printed separately.\n */\nfunction formatCauseChain(error: unknown): string[] {\n const lines: string[] = []\n let cause: unknown = error instanceof Error ? error.cause : undefined\n\n while (cause instanceof Error) {\n lines.push(`Caused by: ${cause.name}: ${cause.message}`)\n if ('code' in cause && typeof cause.code === 'string') {\n lines.push(`Code: ${cause.code}`)\n }\n cause = cause.cause\n }\n\n return lines\n}\n\nexport type RunMigrationFlags = RunMigrationCommand['flags']\n\nexport class RunMigrationCommand extends SanityCommand<typeof RunMigrationCommand> {\n static override args = {\n id: Args.string({\n description: 'ID',\n required: false,\n }),\n }\n\n static override description = 'Run a migration against a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> <id>',\n description: 'dry run the migration',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> <id> --no-dry-run --project xyz --dataset staging',\n description: 'execute the migration against a dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> <id> --from-export=production.tar.gz --no-dry-run --project xyz --dataset staging',\n description: 'execute the migration using a dataset export as the source',\n },\n ]\n\n static override flags = {\n 'api-version': Flags.string({\n description: `API version to use when migrating. Defaults to ${DEFAULT_API_VERSION}.`,\n }),\n concurrency: Flags.integer({\n default: DEFAULT_MUTATION_CONCURRENCY,\n description: `How many mutation requests to run in parallel. Must be between 1 and ${MAX_MUTATION_CONCURRENCY}. Default: ${DEFAULT_MUTATION_CONCURRENCY}.`,\n }),\n confirm: Flags.boolean({\n allowNo: true,\n default: true,\n description:\n 'Prompt for confirmation before running the migration (default: true). Use --no-confirm to skip.',\n }),\n dataset: Flags.string({\n description:\n 'Dataset to migrate. Defaults to the dataset configured in your Sanity CLI config.',\n }),\n 'dry-run': Flags.boolean({\n allowNo: true,\n default: true,\n description:\n 'By default the migration runs in dry mode. Use --no-dry-run to migrate dataset.',\n }),\n 'from-export': Flags.string({\n description:\n 'Use a local dataset export as source for migration instead of calling the Sanity API. Note: this is only supported for dry runs.',\n }),\n progress: Flags.boolean({\n allowNo: true,\n default: true,\n description:\n 'Display progress during migration (default: true). Use --no-progress to hide output.',\n }),\n project: Flags.string({\n description:\n 'Project ID of the dataset to migrate. Defaults to the projectId configured in your Sanity CLI config.',\n }),\n }\n\n static override hiddenAliases = ['migration:run']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(RunMigrationCommand)\n const {directory: workDir} = await this.getProjectRoot()\n const id = args.id\n const migrationsDirectoryPath = path.join(workDir, MIGRATIONS_DIRECTORY)\n\n // Listing migrations (no id given) only needs a project root, mirroring\n // `sanity migrations list` — it must not require api.projectId/dataset.\n if (!id) {\n this.warn(styleText('red', 'Error: Migration ID must be provided'))\n\n let migrations: Awaited<ReturnType<typeof resolveMigrations>> = []\n try {\n migrations = await resolveMigrations(workDir)\n } catch (error) {\n // A missing migrations folder is expected for a fresh project; surface\n // any other failure (e.g. a syntax error in a migration file).\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n this.error(\n `Failed to list migrations: ${error instanceof Error ? error.message : String(error)}`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n\n if (migrations.length === 0) {\n this.log('\\nNo migrations found in the project')\n this.log(\n `Run ${styleText('green', '`sanity migration create <NAME>`')} to create a new migration`,\n )\n this.exit(exitCodes.RUNTIME_ERROR)\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'title', title: 'Title'},\n ],\n title: `Migrations found in project`,\n })\n\n for (const definedMigration of migrations) {\n table.addRow({id: definedMigration.id, title: definedMigration.migration.title})\n }\n this.log(table.render())\n this.log('\\nRun `sanity migration run <ID>` to run a migration')\n\n this.exit(exitCodes.RUNTIME_ERROR)\n }\n\n const cliConfig = await this.getCliConfig()\n const projectId = await this.getProjectId({deprecatedFlagName: 'project'})\n const datasetFromConfig = cliConfig.api?.dataset\n\n const fromExport = flags['from-export']\n const dry = flags['dry-run']\n const dataset = flags.dataset\n const project = flags.project\n const apiVersion = ensureApiVersionFormat(flags['api-version'] ?? DEFAULT_API_VERSION)\n\n if ((dataset && !project) || (project && !dataset)) {\n this.error('If either --dataset or --project is provided, both must be provided', {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (!project && !projectId) {\n this.error(\n 'sanity.cli.js does not contain a project identifier (\"api.projectId\") and no --project option was provided.',\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n if (!dataset && !datasetFromConfig) {\n this.error(\n 'sanity.cli.js does not contain a dataset identifier (\"api.dataset\") and no --dataset option was provided.',\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n const candidates = await resolveMigrationScript(workDir, id)\n const resolvedScripts = candidates.filter((candidate) => isLoadableMigrationScript(candidate))\n\n if (resolvedScripts.length > 1) {\n // todo: consider prompt user about which one to run? note: it's likely a mistake if multiple files resolve to the same name\n this.error(\n `Found multiple migrations for \"${id}\" in ${styleText('cyan', migrationsDirectoryPath)}: \\n - ${candidates\n .map((candidate) => path.relative(migrationsDirectoryPath, candidate.absolutePath))\n .join('\\n - ')}`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n const script = resolvedScripts[0]\n if (!script) {\n this.error(\n `No migration found for \"${id}\" in ${styleText('cyan', migrationsDirectoryPath)}. Make sure that the migration file exists and exports a valid migration as its default export.\\n\n Tried the following files:\\n - ${candidates\n .map((candidate) => path.relative(migrationsDirectoryPath, candidate.absolutePath))\n .join('\\n - ')}`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n const mod = script.mod\n if ('up' in mod || 'down' in mod) {\n // todo: consider adding support for up/down as separate named exports\n // For now, make sure we reserve the names for future use\n this.error(\n 'Named \"up\"/\"down\" migration exports are not supported at this time, please export your migration as the default export',\n {\n exit: exitCodes.RUNTIME_ERROR,\n },\n )\n }\n\n const migration: Migration = mod.default\n\n if (fromExport && !dry) {\n this.error('Can only dry run migrations from a dataset export file', {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const concurrency = flags.concurrency\n if (concurrency !== undefined) {\n if (concurrency > MAX_MUTATION_CONCURRENCY) {\n this.error(`Concurrency exceeds the maximum allowed value of ${MAX_MUTATION_CONCURRENCY}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (concurrency < 1) {\n this.error(`Concurrency must be a positive number, got ${concurrency}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n const projectClient = await getProjectCliClient({\n apiVersion,\n projectId: (project ?? projectId)!,\n requireUser: true,\n })\n const projectConfig = projectClient.config()\n\n const apiConfig: APIConfig = {\n apiHost: projectConfig.apiHost,\n apiVersion,\n dataset: (dataset ?? datasetFromConfig)!,\n projectId: (project ?? projectId)!,\n token: projectConfig.token!,\n } as const\n if (dry) {\n await this.dryRunHandler(id, migration, apiConfig, fromExport)\n return\n }\n\n this.log(\n `\\n${styleText(['yellow', 'bold'], 'Note: During migrations, your webhooks stay active.')}`,\n )\n this.log(\n `To adjust them, launch the management interface with ${styleText('cyan', 'sanity manage')}, navigate to the API settings, and toggle the webhooks before and after the migration as needed.\\n`,\n )\n\n if (flags.confirm) {\n await this.promptConfirmMigrate(apiConfig)\n }\n\n const spin = spinner(`Running migration \"${id}\"`).start()\n const onProgress = this.createProgress(spin, flags, id, dry, apiConfig, migration)\n let lastProgress: MigrationProgress | undefined\n\n try {\n await run(\n {\n api: apiConfig,\n concurrency,\n onProgress: (progress) => {\n lastProgress = progress\n onProgress(progress)\n },\n },\n migration,\n )\n } catch (err) {\n // ora's `stop()` clears the spinner region, taking the multi-line progress\n // block with it. Persist a failure summary first, so the transaction counts\n // (the only record of what was committed before the failure) survive.\n this.persistFailureSummary(spin, flags, id, apiConfig, lastProgress)\n\n if (err instanceof UnknownTransactionOutcomeError) {\n // oclif hard-wraps error messages, which splits the transaction log URLs\n // across lines and makes them impossible to copy. Print the recovery\n // instructions ourselves and rethrow something short for the exit code.\n this.logToStderr(err.message)\n for (const line of formatCauseChain(err)) {\n this.logToStderr(line)\n }\n\n const count = err.transactionIds.length\n this.error(\n `Migration \"${id}\" failed: the ${count === 1 ? 'outcome' : 'outcomes'} of ${count} ${\n count === 1 ? 'transaction' : 'transactions'\n } ${count === 1 ? 'is' : 'are'} unknown. See the transaction log details above.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n throw err\n } finally {\n spin.stop()\n }\n }\n\n private createProgress(\n progressSpinner: ReturnType<typeof spinner>,\n flags: RunMigrationFlags,\n id: string,\n dry: boolean,\n apiConfig: APIConfig,\n migration: Migration,\n ) {\n return function onProgress(progress: MigrationProgress) {\n if (!flags.progress) {\n progressSpinner.stop()\n return\n }\n if (progress.done) {\n progressSpinner.text = `Migration \"${id}\" completed.\n\n Project id: ${styleText('bold', apiConfig.projectId)}\n Dataset: ${styleText('bold', apiConfig.dataset)}\n\n ${progress.documents} documents processed.\n ${progress.mutations} mutations generated.\n ${styleText('green', String(progress.completedTransactions.length))} transactions committed.`\n progressSpinner.stopAndPersist({symbol: styleText('green', '✔')})\n return\n }\n\n for (const transaction of [null, ...progress.currentTransactions]) {\n progressSpinner.text = `Running migration \"${id}\" ${dry ? 'in dry mode...' : '...'}\n\n Project id: ${styleText('bold', apiConfig.projectId)}\n Dataset: ${styleText('bold', apiConfig.dataset)}\n Document type: ${styleText('bold', migration.documentTypes?.join(',') ?? '')}\n\n ${progress.documents} documents processed…\n ${progress.mutations} mutations generated…\n ${styleText('blue', String(progress.pending))} requests pending…\n ${styleText('green', String(progress.completedTransactions.length))} transactions committed.\n\n ${\n transaction && !progress.done\n ? `» ${prettyFormat({indentSize: 2, migration, subject: transaction})}`\n : ''\n }`\n }\n }\n }\n\n private async dryRunHandler(\n id: string,\n migration: Migration,\n apiConfig: APIConfig,\n fromExport: string | undefined,\n ) {\n this.log(`Running migration \"${id}\" in dry mode`)\n\n if (fromExport) {\n this.log(`Using export ${styleText('cyan', fromExport)}`)\n }\n\n this.log()\n this.log(`Project id: ${styleText('bold', apiConfig.projectId)}`)\n this.log(`Dataset: ${styleText('bold', apiConfig.dataset)}`)\n\n for await (const mutation of dryRun({api: apiConfig, exportPath: fromExport}, migration)) {\n if (!mutation) continue\n this.log()\n this.log(\n prettyFormat({\n migration,\n subject: mutation,\n }),\n )\n }\n }\n\n /**\n * Persist the last reported progress with a failure marker.\n *\n * Only the `progress.done` path finalizes the spinner on its own, so without\n * this the entire progress block is wiped when the migration fails partway.\n */\n private persistFailureSummary(\n progressSpinner: ReturnType<typeof spinner>,\n flags: RunMigrationFlags,\n id: string,\n apiConfig: APIConfig,\n progress: MigrationProgress | undefined,\n ) {\n // With `--no-progress` the spinner was already stopped and nothing was ever\n // rendered; a `done` progress event has already persisted its own summary.\n if (!flags.progress || !progress || progress.done) return\n\n progressSpinner.text = `Migration \"${id}\" failed.\n\n Project id: ${styleText('bold', apiConfig.projectId)}\n Dataset: ${styleText('bold', apiConfig.dataset)}\n\n ${progress.documents} documents processed.\n ${progress.mutations} mutations generated.\n ${styleText('green', String(progress.completedTransactions.length))} transactions committed.`\n progressSpinner.stopAndPersist({symbol: styleText('red', '✖')})\n }\n\n private async promptConfirmMigrate(apiConfig: APIConfig) {\n const response = await confirm({\n message: `This migration will run on the ${styleText(\n ['yellow', 'bold'],\n apiConfig.dataset,\n )} dataset in ${styleText(['yellow', 'bold'], apiConfig.projectId)} project. Are you sure?`,\n })\n\n if (!response) {\n runMigrationDebug('User aborted migration')\n this.exit(exitCodes.RUNTIME_ERROR)\n }\n }\n}\n"],"names":["path","styleText","Args","Flags","exitCodes","getProjectCliClient","SanityCommand","subdebug","confirm","spinner","DEFAULT_MUTATION_CONCURRENCY","dryRun","MAX_MUTATION_CONCURRENCY","run","UnknownTransactionOutcomeError","DEFAULT_API_VERSION","MIGRATIONS_DIRECTORY","ensureApiVersionFormat","prettyFormat","resolveMigrations","isLoadableMigrationScript","resolveMigrationScript","Table","runMigrationDebug","formatCauseChain","error","lines","cause","Error","undefined","push","name","message","code","RunMigrationCommand","args","id","string","description","required","examples","command","flags","concurrency","integer","default","boolean","allowNo","dataset","progress","project","hiddenAliases","parse","directory","workDir","getProjectRoot","migrationsDirectoryPath","join","warn","migrations","String","exit","RUNTIME_ERROR","length","log","table","columns","alignment","title","definedMigration","addRow","migration","render","cliConfig","getCliConfig","projectId","getProjectId","deprecatedFlagName","datasetFromConfig","api","fromExport","dry","apiVersion","candidates","resolvedScripts","filter","candidate","map","relative","absolutePath","script","mod","projectClient","requireUser","projectConfig","config","apiConfig","apiHost","token","dryRunHandler","promptConfirmMigrate","spin","start","onProgress","createProgress","lastProgress","err","persistFailureSummary","logToStderr","line","count","transactionIds","stop","progressSpinner","done","text","documents","mutations","completedTransactions","stopAndPersist","symbol","transaction","currentTransactions","documentTypes","pending","indentSize","subject","mutation","exportPath","response"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/migrations/run.ts"],"sourcesContent":["import path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, getProjectCliClient, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\nimport {\n type APIConfig,\n DEFAULT_MUTATION_CONCURRENCY,\n dryRun,\n MAX_MUTATION_CONCURRENCY,\n type Migration,\n type MigrationProgress,\n run,\n UnknownTransactionOutcomeError,\n} from '@sanity/migrate'\n\nimport {DEFAULT_API_VERSION, MIGRATIONS_DIRECTORY} from '../../actions/migration/constants.js'\nimport {ensureApiVersionFormat} from '../../actions/migration/ensureApiVersionFormat.js'\nimport {prettyFormat} from '../../actions/migration/prettyMutationFormatter.js'\nimport {resolveMigrations} from '../../actions/migration/resolveMigrations.js'\nimport {\n isLoadableMigrationScript,\n resolveMigrationScript,\n} from '../../actions/migration/resolveMigrationScript.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst runMigrationDebug = subdebug('migration:run')\n\n/**\n * Renders the `cause` chain of an error the way oclif's own error printer would.\n *\n * Used when printing an error ourselves to bypass oclif's hard word wrapping:\n * the underlying transport error (a timeout, a dropped connection) is the part\n * that explains _why_ the outcome is unknown, so it should not be dropped just\n * because the top-level message is printed separately.\n */\nfunction formatCauseChain(error: unknown): string[] {\n const lines: string[] = []\n let cause: unknown = error instanceof Error ? error.cause : undefined\n\n while (cause instanceof Error) {\n lines.push(`Caused by: ${cause.name}: ${cause.message}`)\n if ('code' in cause && typeof cause.code === 'string') {\n lines.push(`Code: ${cause.code}`)\n }\n cause = cause.cause\n }\n\n return lines\n}\n\nexport type RunMigrationFlags = RunMigrationCommand['flags']\n\nexport class RunMigrationCommand extends SanityCommand<typeof RunMigrationCommand> {\n static override args = {\n id: Args.string({\n description: 'ID',\n required: false,\n }),\n }\n\n static override description = 'Run a migration against a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> <id>',\n description: 'dry run the migration',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> <id> --no-dry-run --project xyz --dataset staging',\n description: 'execute the migration against a dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> <id> --from-export=production.tar.gz --no-dry-run --project xyz --dataset staging',\n description: 'execute the migration using a dataset export as the source',\n },\n ]\n\n static override flags = {\n 'api-version': Flags.string({\n description: `API version to use when migrating. Defaults to ${DEFAULT_API_VERSION}.`,\n }),\n concurrency: Flags.integer({\n default: DEFAULT_MUTATION_CONCURRENCY,\n description: `How many mutation requests to run in parallel. Must be between 1 and ${MAX_MUTATION_CONCURRENCY}. Default: ${DEFAULT_MUTATION_CONCURRENCY}.`,\n }),\n confirm: Flags.boolean({\n allowNo: true,\n default: true,\n description:\n 'Prompt for confirmation before running the migration (default: true). Use --no-confirm to skip.',\n }),\n dataset: Flags.string({\n description:\n 'Dataset to migrate. Defaults to the dataset configured in your Sanity CLI config.',\n }),\n 'dry-run': Flags.boolean({\n allowNo: true,\n default: true,\n description:\n 'By default the migration runs in dry mode. Use --no-dry-run to migrate dataset.',\n }),\n 'from-export': Flags.string({\n description:\n 'Use a local dataset export as source for migration instead of calling the Sanity API. Note: this is only supported for dry runs.',\n }),\n progress: Flags.boolean({\n allowNo: true,\n default: true,\n description:\n 'Display progress during migration (default: true). Use --no-progress to hide output.',\n }),\n ...getProjectIdFlag({\n description: 'Project ID of the dataset to migrate',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases = ['migration:run']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(RunMigrationCommand)\n const {directory: workDir} = await this.getProjectRoot()\n const id = args.id\n const migrationsDirectoryPath = path.join(workDir, MIGRATIONS_DIRECTORY)\n\n // Listing migrations (no id given) only needs a project root, mirroring\n // `sanity migrations list` — it must not require api.projectId/dataset.\n if (!id) {\n this.warn(styleText('red', 'Error: Migration ID must be provided'))\n\n let migrations: Awaited<ReturnType<typeof resolveMigrations>> = []\n try {\n migrations = await resolveMigrations(workDir)\n } catch (error) {\n // A missing migrations folder is expected for a fresh project; surface\n // any other failure (e.g. a syntax error in a migration file).\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n this.error(\n `Failed to list migrations: ${error instanceof Error ? error.message : String(error)}`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n\n if (migrations.length === 0) {\n this.log('\\nNo migrations found in the project')\n this.log(\n `Run ${styleText('green', '`sanity migration create <NAME>`')} to create a new migration`,\n )\n this.exit(exitCodes.RUNTIME_ERROR)\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'title', title: 'Title'},\n ],\n title: `Migrations found in project`,\n })\n\n for (const definedMigration of migrations) {\n table.addRow({id: definedMigration.id, title: definedMigration.migration.title})\n }\n this.log(table.render())\n this.log('\\nRun `sanity migration run <ID>` to run a migration')\n\n this.exit(exitCodes.RUNTIME_ERROR)\n }\n\n const cliConfig = await this.getCliConfig()\n const projectId = await this.getProjectId()\n const datasetFromConfig = cliConfig.api?.dataset\n\n const fromExport = flags['from-export']\n const dry = flags['dry-run']\n const dataset = flags.dataset\n const project = flags.project\n const apiVersion = ensureApiVersionFormat(flags['api-version'] ?? DEFAULT_API_VERSION)\n\n if ((dataset && !project) || (project && !dataset)) {\n this.error('If either --dataset or --project is provided, both must be provided', {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (!project && !projectId) {\n this.error(\n 'sanity.cli.js does not contain a project identifier (\"api.projectId\") and no --project option was provided.',\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n if (!dataset && !datasetFromConfig) {\n this.error(\n 'sanity.cli.js does not contain a dataset identifier (\"api.dataset\") and no --dataset option was provided.',\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n const candidates = await resolveMigrationScript(workDir, id)\n const resolvedScripts = candidates.filter((candidate) => isLoadableMigrationScript(candidate))\n\n if (resolvedScripts.length > 1) {\n // todo: consider prompt user about which one to run? note: it's likely a mistake if multiple files resolve to the same name\n this.error(\n `Found multiple migrations for \"${id}\" in ${styleText('cyan', migrationsDirectoryPath)}: \\n - ${candidates\n .map((candidate) => path.relative(migrationsDirectoryPath, candidate.absolutePath))\n .join('\\n - ')}`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n const script = resolvedScripts[0]\n if (!script) {\n this.error(\n `No migration found for \"${id}\" in ${styleText('cyan', migrationsDirectoryPath)}. Make sure that the migration file exists and exports a valid migration as its default export.\\n\n Tried the following files:\\n - ${candidates\n .map((candidate) => path.relative(migrationsDirectoryPath, candidate.absolutePath))\n .join('\\n - ')}`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n const mod = script.mod\n if ('up' in mod || 'down' in mod) {\n // todo: consider adding support for up/down as separate named exports\n // For now, make sure we reserve the names for future use\n this.error(\n 'Named \"up\"/\"down\" migration exports are not supported at this time, please export your migration as the default export',\n {\n exit: exitCodes.RUNTIME_ERROR,\n },\n )\n }\n\n const migration: Migration = mod.default\n\n if (fromExport && !dry) {\n this.error('Can only dry run migrations from a dataset export file', {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const concurrency = flags.concurrency\n if (concurrency !== undefined) {\n if (concurrency > MAX_MUTATION_CONCURRENCY) {\n this.error(`Concurrency exceeds the maximum allowed value of ${MAX_MUTATION_CONCURRENCY}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (concurrency < 1) {\n this.error(`Concurrency must be a positive number, got ${concurrency}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n const projectClient = await getProjectCliClient({\n apiVersion,\n projectId: (project ?? projectId)!,\n requireUser: true,\n })\n const projectConfig = projectClient.config()\n\n const apiConfig: APIConfig = {\n apiHost: projectConfig.apiHost,\n apiVersion,\n dataset: (dataset ?? datasetFromConfig)!,\n projectId: (project ?? projectId)!,\n token: projectConfig.token!,\n } as const\n if (dry) {\n await this.dryRunHandler(id, migration, apiConfig, fromExport)\n return\n }\n\n this.log(\n `\\n${styleText(['yellow', 'bold'], 'Note: During migrations, your webhooks stay active.')}`,\n )\n this.log(\n `To adjust them, launch the management interface with ${styleText('cyan', 'sanity manage')}, navigate to the API settings, and toggle the webhooks before and after the migration as needed.\\n`,\n )\n\n if (flags.confirm) {\n await this.promptConfirmMigrate(apiConfig)\n }\n\n const spin = spinner(`Running migration \"${id}\"`).start()\n const onProgress = this.createProgress(spin, flags, id, dry, apiConfig, migration)\n let lastProgress: MigrationProgress | undefined\n\n try {\n await run(\n {\n api: apiConfig,\n concurrency,\n onProgress: (progress) => {\n lastProgress = progress\n onProgress(progress)\n },\n },\n migration,\n )\n } catch (err) {\n // ora's `stop()` clears the spinner region, taking the multi-line progress\n // block with it. Persist a failure summary first, so the transaction counts\n // (the only record of what was committed before the failure) survive.\n this.persistFailureSummary(spin, flags, id, apiConfig, lastProgress)\n\n if (err instanceof UnknownTransactionOutcomeError) {\n // oclif hard-wraps error messages, which splits the transaction log URLs\n // across lines and makes them impossible to copy. Print the recovery\n // instructions ourselves and rethrow something short for the exit code.\n this.logToStderr(err.message)\n for (const line of formatCauseChain(err)) {\n this.logToStderr(line)\n }\n\n const count = err.transactionIds.length\n this.error(\n `Migration \"${id}\" failed: the ${count === 1 ? 'outcome' : 'outcomes'} of ${count} ${\n count === 1 ? 'transaction' : 'transactions'\n } ${count === 1 ? 'is' : 'are'} unknown. See the transaction log details above.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n throw err\n } finally {\n spin.stop()\n }\n }\n\n private createProgress(\n progressSpinner: ReturnType<typeof spinner>,\n flags: RunMigrationFlags,\n id: string,\n dry: boolean,\n apiConfig: APIConfig,\n migration: Migration,\n ) {\n return function onProgress(progress: MigrationProgress) {\n if (!flags.progress) {\n progressSpinner.stop()\n return\n }\n if (progress.done) {\n progressSpinner.text = `Migration \"${id}\" completed.\n\n Project id: ${styleText('bold', apiConfig.projectId)}\n Dataset: ${styleText('bold', apiConfig.dataset)}\n\n ${progress.documents} documents processed.\n ${progress.mutations} mutations generated.\n ${styleText('green', String(progress.completedTransactions.length))} transactions committed.`\n progressSpinner.stopAndPersist({symbol: styleText('green', '✔')})\n return\n }\n\n for (const transaction of [null, ...progress.currentTransactions]) {\n progressSpinner.text = `Running migration \"${id}\" ${dry ? 'in dry mode...' : '...'}\n\n Project id: ${styleText('bold', apiConfig.projectId)}\n Dataset: ${styleText('bold', apiConfig.dataset)}\n Document type: ${styleText('bold', migration.documentTypes?.join(',') ?? '')}\n\n ${progress.documents} documents processed…\n ${progress.mutations} mutations generated…\n ${styleText('blue', String(progress.pending))} requests pending…\n ${styleText('green', String(progress.completedTransactions.length))} transactions committed.\n\n ${\n transaction && !progress.done\n ? `» ${prettyFormat({indentSize: 2, migration, subject: transaction})}`\n : ''\n }`\n }\n }\n }\n\n private async dryRunHandler(\n id: string,\n migration: Migration,\n apiConfig: APIConfig,\n fromExport: string | undefined,\n ) {\n this.log(`Running migration \"${id}\" in dry mode`)\n\n if (fromExport) {\n this.log(`Using export ${styleText('cyan', fromExport)}`)\n }\n\n this.log()\n this.log(`Project id: ${styleText('bold', apiConfig.projectId)}`)\n this.log(`Dataset: ${styleText('bold', apiConfig.dataset)}`)\n\n for await (const mutation of dryRun({api: apiConfig, exportPath: fromExport}, migration)) {\n if (!mutation) continue\n this.log()\n this.log(\n prettyFormat({\n migration,\n subject: mutation,\n }),\n )\n }\n }\n\n /**\n * Persist the last reported progress with a failure marker.\n *\n * Only the `progress.done` path finalizes the spinner on its own, so without\n * this the entire progress block is wiped when the migration fails partway.\n */\n private persistFailureSummary(\n progressSpinner: ReturnType<typeof spinner>,\n flags: RunMigrationFlags,\n id: string,\n apiConfig: APIConfig,\n progress: MigrationProgress | undefined,\n ) {\n // With `--no-progress` the spinner was already stopped and nothing was ever\n // rendered; a `done` progress event has already persisted its own summary.\n if (!flags.progress || !progress || progress.done) return\n\n progressSpinner.text = `Migration \"${id}\" failed.\n\n Project id: ${styleText('bold', apiConfig.projectId)}\n Dataset: ${styleText('bold', apiConfig.dataset)}\n\n ${progress.documents} documents processed.\n ${progress.mutations} mutations generated.\n ${styleText('green', String(progress.completedTransactions.length))} transactions committed.`\n progressSpinner.stopAndPersist({symbol: styleText('red', '✖')})\n }\n\n private async promptConfirmMigrate(apiConfig: APIConfig) {\n const response = await confirm({\n message: `This migration will run on the ${styleText(\n ['yellow', 'bold'],\n apiConfig.dataset,\n )} dataset in ${styleText(['yellow', 'bold'], apiConfig.projectId)} project. Are you sure?`,\n })\n\n if (!response) {\n runMigrationDebug('User aborted migration')\n this.exit(exitCodes.RUNTIME_ERROR)\n }\n }\n}\n"],"names":["path","styleText","Args","Flags","exitCodes","getProjectCliClient","SanityCommand","subdebug","confirm","spinner","DEFAULT_MUTATION_CONCURRENCY","dryRun","MAX_MUTATION_CONCURRENCY","run","UnknownTransactionOutcomeError","DEFAULT_API_VERSION","MIGRATIONS_DIRECTORY","ensureApiVersionFormat","prettyFormat","resolveMigrations","isLoadableMigrationScript","resolveMigrationScript","Table","getProjectIdFlag","runMigrationDebug","formatCauseChain","error","lines","cause","Error","undefined","push","name","message","code","RunMigrationCommand","args","id","string","description","required","examples","command","flags","concurrency","integer","default","boolean","allowNo","dataset","progress","semantics","hiddenAliases","parse","directory","workDir","getProjectRoot","migrationsDirectoryPath","join","warn","migrations","String","exit","RUNTIME_ERROR","length","log","table","columns","alignment","title","definedMigration","addRow","migration","render","cliConfig","getCliConfig","projectId","getProjectId","datasetFromConfig","api","fromExport","dry","project","apiVersion","candidates","resolvedScripts","filter","candidate","map","relative","absolutePath","script","mod","projectClient","requireUser","projectConfig","config","apiConfig","apiHost","token","dryRunHandler","promptConfirmMigrate","spin","start","onProgress","createProgress","lastProgress","err","persistFailureSummary","logToStderr","line","count","transactionIds","stop","progressSpinner","done","text","documents","mutations","completedTransactions","stopAndPersist","symbol","transaction","currentTransactions","documentTypes","pending","indentSize","subject","mutation","exportPath","response"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Flags } from '@oclif/core';
|
|
2
1
|
import { exitCodes, SanityCommand } from '@sanity/cli-core';
|
|
3
2
|
import { logSymbols } from '@sanity/cli-core/ux';
|
|
4
3
|
import { formatKeyValue, sectionHeader } from '../../actions/debug/output.js';
|
|
5
4
|
import { getProjectClaimStatus } from '../../services/projects.js';
|
|
6
5
|
import { Table } from '../../util/responsiveTable.js';
|
|
6
|
+
import { getProjectIdFlag } from '../../util/sharedFlags.js';
|
|
7
7
|
import { readUnclaimedProjects, removeUnclaimedProject } from '../../util/unclaimedProjects.js';
|
|
8
8
|
function isExpired(record) {
|
|
9
9
|
return Date.parse(record.expiresAt) <= Date.now();
|
|
@@ -16,13 +16,14 @@ export class UnclaimedProjectsCommand extends SanityCommand {
|
|
|
16
16
|
description: 'List locally recorded unclaimed projects'
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
|
-
command: '<%= config.bin %> <%= command.id %> --project
|
|
19
|
+
command: '<%= config.bin %> <%= command.id %> --project abc123',
|
|
20
20
|
description: 'Show recovery details for one project'
|
|
21
21
|
}
|
|
22
22
|
];
|
|
23
23
|
static flags = {
|
|
24
|
-
|
|
25
|
-
description: 'Project ID to recover'
|
|
24
|
+
...getProjectIdFlag({
|
|
25
|
+
description: 'Project ID to recover',
|
|
26
|
+
semantics: 'specify'
|
|
26
27
|
})
|
|
27
28
|
};
|
|
28
29
|
static hiddenAliases = [
|
|
@@ -38,7 +39,7 @@ export class UnclaimedProjectsCommand extends SanityCommand {
|
|
|
38
39
|
});
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
|
-
const projectId = this.flags
|
|
42
|
+
const projectId = this.flags.project;
|
|
42
43
|
if (projectId) {
|
|
43
44
|
const record = records.find((candidate)=>candidate.projectId === projectId);
|
|
44
45
|
if (!record) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/projects/unclaimed.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {formatKeyValue, sectionHeader} from '../../actions/debug/output.js'\nimport {getProjectClaimStatus} from '../../services/projects.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {\n readUnclaimedProjects,\n removeUnclaimedProject,\n type UnclaimedProjectRecord,\n} from '../../util/unclaimedProjects.js'\n\nfunction isExpired(record: UnclaimedProjectRecord): boolean {\n return Date.parse(record.expiresAt) <= Date.now()\n}\n\nexport class UnclaimedProjectsCommand extends SanityCommand<typeof UnclaimedProjectsCommand> {\n static override description = 'Recover details for unclaimed projects created on this machine'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List locally recorded unclaimed projects',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'Show recovery details for one project',\n },\n ]\n\n static override flags = {\n 'project-id': Flags.string({\n description: 'Project ID to recover',\n }),\n }\n\n static override hiddenAliases: string[] = ['project:unclaimed']\n\n public async run(): Promise<void> {\n let records: UnclaimedProjectRecord[]\n try {\n records = readUnclaimedProjects()\n } catch (error) {\n this.output.error(\n `Could not read local unclaimed projects: ${\n error instanceof Error ? error.message : String(error)\n } Remove or repair the \"unclaimedProjects\" entry in your Sanity user config.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n return\n }\n\n const projectId = this.flags['project-id']\n if (projectId) {\n const record = records.find((candidate) => candidate.projectId === projectId)\n if (!record) {\n this.output.error(\n `No local recovery record found for project \"${projectId}\". Run \\`sanity projects unclaimed\\` to list recorded projects.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n return\n }\n\n if (isExpired(record)) {\n if (this.removeExpiredProject(record)) {\n this.logExpiredProject(record.projectId)\n }\n return\n }\n\n const status = await getProjectClaimStatus(record.projectId, record.token)\n if (status === 'claimed') {\n if (this.removeClaimedProject(record)) {\n this.logClaimedProject(record.projectId)\n }\n return\n }\n if (status === 'unknown') {\n this.output.warn(\n `Could not verify whether project \"${projectId}\" is claimed. Local recovery details were kept. Run this command again to retry.`,\n )\n }\n\n this.output.log(sectionHeader('Project'))\n const details = [\n ['Project ID', record.projectId],\n ['Dataset', record.dataset],\n ['Created', record.mintedAt],\n ['Claim deadline', record.expiresAt],\n ['Claim URL', record.claimUrl],\n ['Access token', record.token],\n ]\n for (const [label, value] of details) {\n this.output.log(formatKeyValue(label, value, {padTo: 14}))\n }\n this.output.log()\n return\n }\n\n const expiredProjectIds: string[] = []\n const activeRecords = records.filter((record) => {\n if (!isExpired(record)) return true\n if (this.removeExpiredProject(record)) {\n expiredProjectIds.push(record.projectId)\n }\n return false\n })\n const checked = await Promise.all(\n activeRecords.map(async (record) => ({\n record,\n status: await getProjectClaimStatus(record.projectId, record.token),\n })),\n )\n records = []\n const claimedProjectIds: string[] = []\n for (const {record, status} of checked) {\n if (status === 'claimed') {\n if (this.removeClaimedProject(record)) {\n claimedProjectIds.push(record.projectId)\n }\n continue\n }\n if (status === 'unknown') {\n this.output.warn(\n `Could not verify whether project \"${record.projectId}\" is claimed. Local recovery details were kept. Run this command again to retry.`,\n )\n }\n records.push(record)\n }\n\n if (records.length === 0) {\n if (claimedProjectIds.length > 0 || expiredProjectIds.length > 0) {\n for (const expiredProjectId of expiredProjectIds) {\n this.logExpiredProject(expiredProjectId)\n }\n for (const claimedProjectId of claimedProjectIds) {\n this.logClaimedProject(claimedProjectId)\n }\n return\n }\n this.output.log(\n 'No locally recorded unclaimed projects. Create one with `sanity new`, then return here if you need its recovery details.',\n )\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'dataset', title: 'Dataset'},\n {alignment: 'left', name: 'created', title: 'Created'},\n {alignment: 'left', name: 'claimDeadline', title: 'Claim deadline'},\n {alignment: 'left', name: 'claimUrl', title: 'Claim URL'},\n ],\n })\n for (const record of records) {\n table.addRow({\n claimDeadline: record.expiresAt,\n claimUrl: record.claimUrl,\n created: record.mintedAt,\n dataset: record.dataset,\n id: record.projectId,\n })\n }\n this.output.log(table.render())\n if (claimedProjectIds.length > 0 || expiredProjectIds.length > 0) {\n this.output.log()\n for (const expiredProjectId of expiredProjectIds) {\n this.logExpiredProject(expiredProjectId)\n }\n for (const claimedProjectId of claimedProjectIds) {\n this.logClaimedProject(claimedProjectId)\n }\n }\n }\n\n private logClaimedProject(projectId: string): void {\n this.output.log(\n `${logSymbols.success} Project \"${projectId}\" is claimed. Local recovery details removed.`,\n )\n }\n\n private logExpiredProject(projectId: string): void {\n this.output.log(\n `${logSymbols.success} Project \"${projectId}\" expired. Local recovery details removed.`,\n )\n }\n\n private removeClaimedProject(record: UnclaimedProjectRecord): boolean {\n const removed = removeUnclaimedProject(record.projectId, record.claimToken)\n if (!removed) {\n this.output.warn(\n `Project \"${record.projectId}\" is claimed, but its local recovery details could not be removed. Run this command again to retry.`,\n )\n }\n return removed\n }\n\n private removeExpiredProject(record: UnclaimedProjectRecord): boolean {\n const removed = removeUnclaimedProject(record.projectId, record.claimToken)\n if (!removed) {\n this.output.warn(\n `Project \"${record.projectId}\" expired, but its local recovery details could not be removed. Run this command again to retry.`,\n )\n }\n return removed\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","logSymbols","formatKeyValue","sectionHeader","getProjectClaimStatus","Table","readUnclaimedProjects","removeUnclaimedProject","isExpired","record","Date","parse","expiresAt","now","UnclaimedProjectsCommand","description","examples","command","flags","string","hiddenAliases","run","records","error","output","Error","message","String","exit","RUNTIME_ERROR","projectId","find","candidate","removeExpiredProject","logExpiredProject","status","token","removeClaimedProject","logClaimedProject","warn","log","details","dataset","mintedAt","claimUrl","label","value","padTo","expiredProjectIds","activeRecords","filter","push","checked","Promise","all","map","claimedProjectIds","length","expiredProjectId","claimedProjectId","table","columns","alignment","name","title","addRow","claimDeadline","created","id","render","success","removed","claimToken"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/projects/unclaimed.ts"],"sourcesContent":["import {exitCodes, SanityCommand} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {formatKeyValue, sectionHeader} from '../../actions/debug/output.js'\nimport {getProjectClaimStatus} from '../../services/projects.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\nimport {\n readUnclaimedProjects,\n removeUnclaimedProject,\n type UnclaimedProjectRecord,\n} from '../../util/unclaimedProjects.js'\n\nfunction isExpired(record: UnclaimedProjectRecord): boolean {\n return Date.parse(record.expiresAt) <= Date.now()\n}\n\nexport class UnclaimedProjectsCommand extends SanityCommand<typeof UnclaimedProjectsCommand> {\n static override description = 'Recover details for unclaimed projects created on this machine'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List locally recorded unclaimed projects',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project abc123',\n description: 'Show recovery details for one project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to recover',\n semantics: 'specify',\n }),\n }\n\n static override hiddenAliases: string[] = ['project:unclaimed']\n\n public async run(): Promise<void> {\n let records: UnclaimedProjectRecord[]\n try {\n records = readUnclaimedProjects()\n } catch (error) {\n this.output.error(\n `Could not read local unclaimed projects: ${\n error instanceof Error ? error.message : String(error)\n } Remove or repair the \"unclaimedProjects\" entry in your Sanity user config.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n return\n }\n\n const projectId = this.flags.project\n if (projectId) {\n const record = records.find((candidate) => candidate.projectId === projectId)\n if (!record) {\n this.output.error(\n `No local recovery record found for project \"${projectId}\". Run \\`sanity projects unclaimed\\` to list recorded projects.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n return\n }\n\n if (isExpired(record)) {\n if (this.removeExpiredProject(record)) {\n this.logExpiredProject(record.projectId)\n }\n return\n }\n\n const status = await getProjectClaimStatus(record.projectId, record.token)\n if (status === 'claimed') {\n if (this.removeClaimedProject(record)) {\n this.logClaimedProject(record.projectId)\n }\n return\n }\n if (status === 'unknown') {\n this.output.warn(\n `Could not verify whether project \"${projectId}\" is claimed. Local recovery details were kept. Run this command again to retry.`,\n )\n }\n\n this.output.log(sectionHeader('Project'))\n const details = [\n ['Project ID', record.projectId],\n ['Dataset', record.dataset],\n ['Created', record.mintedAt],\n ['Claim deadline', record.expiresAt],\n ['Claim URL', record.claimUrl],\n ['Access token', record.token],\n ]\n for (const [label, value] of details) {\n this.output.log(formatKeyValue(label, value, {padTo: 14}))\n }\n this.output.log()\n return\n }\n\n const expiredProjectIds: string[] = []\n const activeRecords = records.filter((record) => {\n if (!isExpired(record)) return true\n if (this.removeExpiredProject(record)) {\n expiredProjectIds.push(record.projectId)\n }\n return false\n })\n const checked = await Promise.all(\n activeRecords.map(async (record) => ({\n record,\n status: await getProjectClaimStatus(record.projectId, record.token),\n })),\n )\n records = []\n const claimedProjectIds: string[] = []\n for (const {record, status} of checked) {\n if (status === 'claimed') {\n if (this.removeClaimedProject(record)) {\n claimedProjectIds.push(record.projectId)\n }\n continue\n }\n if (status === 'unknown') {\n this.output.warn(\n `Could not verify whether project \"${record.projectId}\" is claimed. Local recovery details were kept. Run this command again to retry.`,\n )\n }\n records.push(record)\n }\n\n if (records.length === 0) {\n if (claimedProjectIds.length > 0 || expiredProjectIds.length > 0) {\n for (const expiredProjectId of expiredProjectIds) {\n this.logExpiredProject(expiredProjectId)\n }\n for (const claimedProjectId of claimedProjectIds) {\n this.logClaimedProject(claimedProjectId)\n }\n return\n }\n this.output.log(\n 'No locally recorded unclaimed projects. Create one with `sanity new`, then return here if you need its recovery details.',\n )\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'dataset', title: 'Dataset'},\n {alignment: 'left', name: 'created', title: 'Created'},\n {alignment: 'left', name: 'claimDeadline', title: 'Claim deadline'},\n {alignment: 'left', name: 'claimUrl', title: 'Claim URL'},\n ],\n })\n for (const record of records) {\n table.addRow({\n claimDeadline: record.expiresAt,\n claimUrl: record.claimUrl,\n created: record.mintedAt,\n dataset: record.dataset,\n id: record.projectId,\n })\n }\n this.output.log(table.render())\n if (claimedProjectIds.length > 0 || expiredProjectIds.length > 0) {\n this.output.log()\n for (const expiredProjectId of expiredProjectIds) {\n this.logExpiredProject(expiredProjectId)\n }\n for (const claimedProjectId of claimedProjectIds) {\n this.logClaimedProject(claimedProjectId)\n }\n }\n }\n\n private logClaimedProject(projectId: string): void {\n this.output.log(\n `${logSymbols.success} Project \"${projectId}\" is claimed. Local recovery details removed.`,\n )\n }\n\n private logExpiredProject(projectId: string): void {\n this.output.log(\n `${logSymbols.success} Project \"${projectId}\" expired. Local recovery details removed.`,\n )\n }\n\n private removeClaimedProject(record: UnclaimedProjectRecord): boolean {\n const removed = removeUnclaimedProject(record.projectId, record.claimToken)\n if (!removed) {\n this.output.warn(\n `Project \"${record.projectId}\" is claimed, but its local recovery details could not be removed. Run this command again to retry.`,\n )\n }\n return removed\n }\n\n private removeExpiredProject(record: UnclaimedProjectRecord): boolean {\n const removed = removeUnclaimedProject(record.projectId, record.claimToken)\n if (!removed) {\n this.output.warn(\n `Project \"${record.projectId}\" expired, but its local recovery details could not be removed. Run this command again to retry.`,\n )\n }\n return removed\n }\n}\n"],"names":["exitCodes","SanityCommand","logSymbols","formatKeyValue","sectionHeader","getProjectClaimStatus","Table","getProjectIdFlag","readUnclaimedProjects","removeUnclaimedProject","isExpired","record","Date","parse","expiresAt","now","UnclaimedProjectsCommand","description","examples","command","flags","semantics","hiddenAliases","run","records","error","output","Error","message","String","exit","RUNTIME_ERROR","projectId","project","find","candidate","removeExpiredProject","logExpiredProject","status","token","removeClaimedProject","logClaimedProject","warn","log","details","dataset","mintedAt","claimUrl","label","value","padTo","expiredProjectIds","activeRecords","filter","push","checked","Promise","all","map","claimedProjectIds","length","expiredProjectId","claimedProjectId","table","columns","alignment","name","title","addRow","claimDeadline","created","id","render","success","removed","claimToken"],"mappings":"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"}
|
|
@@ -40,7 +40,7 @@ export class CreateTokenCommand extends SanityCommand {
|
|
|
40
40
|
description: 'Create an organization token with Context, Blueprints and project access'
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
|
-
command: '<%= config.bin %> <%= command.id %> "My Token" --project
|
|
43
|
+
command: '<%= config.bin %> <%= command.id %> "My Token" --project abc123 --role=editor',
|
|
44
44
|
description: 'Create a project token with editor role'
|
|
45
45
|
},
|
|
46
46
|
{
|
|
@@ -183,7 +183,7 @@ export class CreateTokenCommand extends SanityCommand {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
async getFlagGrants(grants) {
|
|
186
|
-
const {
|
|
186
|
+
const { project: projectIdFlag, role } = this.flags;
|
|
187
187
|
if (grants.length > 0 && !projectIdFlag && !role) {
|
|
188
188
|
return {
|
|
189
189
|
grants
|
|
@@ -237,7 +237,7 @@ export class CreateTokenCommand extends SanityCommand {
|
|
|
237
237
|
return parsed.grant;
|
|
238
238
|
}
|
|
239
239
|
async promptForAccess(getExpiry) {
|
|
240
|
-
const requestedProjectId = this.flags
|
|
240
|
+
const requestedProjectId = this.flags.project;
|
|
241
241
|
const configProjectId = (await this.tryGetCliConfig()).api?.projectId;
|
|
242
242
|
const projects = await listProjects({
|
|
243
243
|
onlyExplicitMembership: false
|
|
@@ -356,7 +356,7 @@ export class CreateTokenCommand extends SanityCommand {
|
|
|
356
356
|
return await this.getProjectId();
|
|
357
357
|
} catch (error) {
|
|
358
358
|
if (!isProjectRootNotFoundError(error)) throw error;
|
|
359
|
-
this.error('Token access is required. Pass at least one `--grant`, or `--project
|
|
359
|
+
this.error('Token access is required. Pass at least one `--grant`, or `--project` to create a project token.', {
|
|
360
360
|
exit: exitCodes.USAGE_ERROR
|
|
361
361
|
});
|
|
362
362
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/tokens/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage, isProjectRootNotFoundError} from '@sanity/cli-core/errors'\nimport {input, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {\n MissingOrganizationError,\n resolveOrganizationId,\n} from '../../actions/context/resolveOrganizationId.js'\nimport {expiryInDays, parseExpiryDate} from '../../actions/tokens/expiry.js'\nimport {\n DEFAULT_ROLE,\n findGrantConflict,\n formatGrant,\n getProjectTokenMembership,\n type Grant,\n parseGrant,\n projectGrantKey,\n toMemberships,\n} from '../../actions/tokens/grants.js'\nimport {type RobotWithToken} from '../../actions/tokens/types.js'\nimport {validateRole} from '../../actions/tokens/validateRole.js'\nimport {promptForGrants} from '../../prompts/promptForGrants.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listProjects} from '../../services/projects.js'\nimport {createOrganizationToken, createToken} from '../../services/tokens.js'\nimport {getOrganizationFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst tokensCreateDebug = subdebug('tokens:create')\n\ntype Access = {\n expiresAt: string | undefined\n grants: Grant[]\n\n organizationId?: string\n roleTitle?: string\n}\n\n// Mirrors the expiry presets offered by sanity.io/manage\nconst EXPIRY_PRESET_DAYS = [30, 60, 90]\n\nconst ORGANIZATION_FORBIDDEN_MESSAGE =\n 'You need permission to create organization tokens. Grant a single project to create a project token instead.'\n\nexport class CreateTokenCommand extends SanityCommand<typeof CreateTokenCommand> {\n static override args = {\n label: Args.string({\n description: 'Label for the new token',\n required: false,\n }),\n }\n\n static override description =\n 'Create a new API token with access to an organization and its projects'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\"',\n description: 'Create a token, choosing its access interactively',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \"CI Token\" --grant context=developer --grant blueprints --grant project:abc123=editor',\n description: 'Create an organization token with Context, Blueprints and project access',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My Token\" --project-id abc123 --role=editor',\n description: 'Create a project token with editor role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"CI Token\" --role=editor --yes',\n description: 'Create a project token in unattended mode',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"Build Token\" --expires-at 2027-01-01',\n description: 'Create a token that expires on a given date',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"API Token\" --json',\n description: 'Output token information as JSON',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to create token in',\n semantics: 'override',\n }),\n ...getOrganizationFlag({\n description: 'Organization to create an organization token in',\n semantics: 'override',\n }),\n 'expires-at': Flags.string({\n description: 'Date or timestamp the token expires (ISO 8601; tokens never expire by default)',\n helpValue: '2027-01-01',\n }),\n grant: Flags.string({\n description:\n 'Access to grant, as <permission>[=<role>] or project:<projectId>=<role>[,<role>]. Permissions: context, blueprints, media-library, canvas, sdk-apps, access, deploy-studios. Repeatable',\n helpValue: 'context=editor',\n multiple: true,\n }),\n role: Flags.string({\n description: 'Role to assign on the project (defaults to viewer in unattended mode)',\n helpValue: 'viewer',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: 'Skip prompts and use defaults (unattended mode)',\n }),\n }\n\n static override hiddenAliases: string[] = ['tokens:add', 'token:add', 'token:create']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CreateTokenCommand)\n const {label: givenLabel} = args\n\n const label = givenLabel === undefined ? await this.promptForLabel() : givenLabel.trim()\n if (!label) {\n this.error('Token label cannot be empty. Pass a non-empty value as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const flagExpiresAt = this.parseExpiryFlag(flags['expires-at'])\n const getExpiry = async () =>\n flags['expires-at'] === undefined ? this.promptForExpiry() : flagExpiresAt\n const flagGrants = (flags.grant ?? []).map((value) => this.parseGrantFlag(value))\n const grantConflict = findGrantConflict(flagGrants)\n if (grantConflict) {\n this.error(grantConflict, {exit: exitCodes.USAGE_ERROR})\n }\n\n const {expiresAt, grants, organizationId, roleTitle}: Access =\n this.isUnattended() || flagGrants.length > 0 || flags.role !== undefined\n ? {...(await this.getFlagGrants(flagGrants)), expiresAt: await getExpiry()}\n : await this.promptForAccess(getExpiry)\n\n let token: RobotWithToken\n try {\n token = await this.createTokenForGrants({\n expiresAt,\n grants,\n label,\n organizationId,\n })\n } catch (error) {\n if (error instanceof CLIError) throw error\n tokensCreateDebug('Error creating token', error)\n this.error(`Token creation failed:\\n${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (flags.json) {\n this.logJson(token)\n return\n }\n\n this.log('API token created')\n this.log(`Label: ${token.label}`)\n this.log(`ID: ${token.id}`)\n if (roleTitle) {\n this.log(`Role: ${roleTitle}`)\n }\n if (token.expiresAt) {\n this.log(`Expires: ${token.expiresAt}`)\n }\n this.log(`Token: ${token.token}`)\n this.log('')\n this.log(\"Copy the token now. It won't be shown again.\")\n\n if (!this.isUnattended()) {\n this.log('')\n this.log('To create the same token without prompts:')\n this.log(` ${formatCreateCommand(this.config.bin, {expiresAt, grants, label, token})}`)\n }\n }\n\n private async createTokenForGrants(options: {\n expiresAt: string | undefined\n grants: Grant[]\n label: string\n organizationId: string | undefined\n }): Promise<RobotWithToken> {\n const {expiresAt, grants, label} = options\n const projectMembership = getProjectTokenMembership(grants)\n if (projectMembership) {\n const {resourceId: projectId, roleNames} = projectMembership\n return createToken({expiresAt, label, projectId, roleNames})\n }\n\n const organizationId = options.organizationId ?? (await this.resolveOrganization())\n try {\n return await createOrganizationToken({\n expiresAt,\n label,\n memberships: toMemberships(grants, organizationId),\n organizationId,\n })\n } catch (error) {\n if (error instanceof ClientError && error.statusCode === 403) {\n throw new Error(ORGANIZATION_FORBIDDEN_MESSAGE, {cause: error})\n }\n throw error\n }\n }\n\n private async getFlagGrants(grants: Grant[]): Promise<{grants: Grant[]; roleTitle?: string}> {\n const {'project-id': projectIdFlag, role} = this.flags\n if (grants.length > 0 && !projectIdFlag && !role) {\n return {grants}\n }\n\n const projectId = await this.resolveProjectId()\n const projectKey = projectGrantKey(projectId)\n if (grants.some((grant) => grant.key === projectKey)) {\n if (role) {\n this.error(\n `Set the role for project ${projectId} with either --role or --grant ${projectKey}=<role>, not both.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n return {grants}\n }\n\n const projectRole = role ? await validateRole(role, projectId, this.output) : DEFAULT_ROLE\n\n return {\n grants: [...grants, {key: projectKey, roles: [projectRole.name]}],\n roleTitle: grants.length === 0 ? projectRole.title : undefined,\n }\n }\n\n private parseExpiryFlag(value: string | undefined): string | undefined {\n if (value === undefined) {\n return undefined\n }\n\n const parsed = parseExpiryDate(value)\n if ('error' in parsed) {\n this.error(parsed.error, {exit: exitCodes.USAGE_ERROR})\n }\n\n return parsed.expiresAt\n }\n\n private parseGrantFlag(value: string): Grant {\n const parsed = parseGrant(value.trim())\n if ('error' in parsed) {\n this.error(parsed.error, {exit: exitCodes.USAGE_ERROR})\n }\n\n return parsed.grant\n }\n\n private async promptForAccess(getExpiry: () => Promise<string | undefined>): Promise<Access> {\n const requestedProjectId = this.flags['project-id']\n const configProjectId = (await this.tryGetCliConfig()).api?.projectId\n const projects = await listProjects({onlyExplicitMembership: false})\n const organizationOf = (projectId: string | undefined) =>\n projects.find((project) => project.id === projectId)?.organizationId ?? undefined\n const organizationId = await this.resolveOrganization({\n fallback: organizationOf(configProjectId),\n preferred: organizationOf(requestedProjectId),\n })\n const organizationProjects = projects.filter(\n (project) => project.organizationId === organizationId,\n )\n if (requestedProjectId && !organizationProjects.some(({id}) => id === requestedProjectId)) {\n this.error(\n `Project ${requestedProjectId} isn't in organization ${organizationId}, or you don't have access to it.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const expiresAt = await getExpiry()\n const grants = await promptForGrants({\n preselectedProjectId: requestedProjectId,\n projects: organizationProjects,\n })\n return {expiresAt, grants, organizationId}\n }\n\n private async promptForExpiry(): Promise<string | undefined> {\n if (this.isUnattended()) {\n return undefined // Tokens never expire by default\n }\n\n const choice = await select({\n choices: [\n {name: 'Never', value: 'never'},\n ...EXPIRY_PRESET_DAYS.map((days) => ({\n name: `${days} days (${expiryInDays(days).slice(0, 10)})`,\n value: String(days),\n })),\n {name: 'Custom date', value: 'custom'},\n ],\n default: 'never',\n message: 'Token expiry:',\n })\n\n if (choice === 'never') {\n return undefined\n }\n\n if (choice === 'custom') {\n const value = await input({\n message: 'Expiry date (ISO 8601, e.g. 2027-01-01):',\n validate: (input) => {\n const parsed = parseExpiryDate(input.trim())\n return 'error' in parsed ? parsed.error : true\n },\n })\n const parsed = parseExpiryDate(value.trim())\n return 'error' in parsed ? undefined : parsed.expiresAt\n }\n\n return expiryInDays(Number(choice))\n }\n\n private async promptForLabel(): Promise<string> {\n if (this.isUnattended()) {\n this.error('Token label is required. Pass it as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const label = await input({\n message: 'Token label:',\n validate: (value) => {\n if (!value || !value.trim()) {\n return 'Label cannot be empty'\n }\n return true\n },\n })\n\n return label.trim()\n }\n\n private async resolveOrganization(\n projectOrganizations: {fallback?: string; preferred?: string} = {},\n ): Promise<string> {\n const {fallback, preferred} = projectOrganizations\n try {\n return await resolveOrganizationId({\n configuredOrganizationId:\n preferred ?? (await this.tryGetCliConfig()).app?.organizationId ?? fallback,\n flagOrganizationId: this.flags.organization,\n unattended: this.isUnattended(),\n })\n } catch (error) {\n tokensCreateDebug('Error resolving organization', error)\n this.error(getErrorMessage(error), {\n exit:\n error instanceof MissingOrganizationError\n ? exitCodes.USAGE_ERROR\n : exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async resolveProjectId(): Promise<string> {\n if (!this.isUnattended()) {\n return this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.roles'},\n {grant: 'create', permission: 'sanity.project.tokens'},\n ],\n }),\n })\n }\n\n try {\n return await this.getProjectId()\n } catch (error) {\n if (!isProjectRootNotFoundError(error)) throw error\n this.error(\n 'Token access is required. Pass at least one `--grant`, or `--project-id` to create a project token.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n }\n}\n\nfunction quoteShellArg(value: string): string {\n return /^[\\w./:@=,-]+$/.test(value) ? value : `'${value.replaceAll(\"'\", String.raw`'\\''`)}'`\n}\n\nfunction formatCreateCommand(\n bin: string,\n {\n expiresAt,\n grants,\n label,\n token,\n }: {\n expiresAt: string | undefined\n grants: Grant[]\n label: string\n token: RobotWithToken\n },\n): string {\n const organizationId =\n token.managedBy?.resourceType === 'organization' ? token.managedBy.resourceId : undefined\n return [\n `${bin} tokens create`,\n quoteShellArg(label),\n ...(organizationId ? [`--organization ${organizationId}`] : []),\n ...(expiresAt ? [`--expires-at ${expiresAt}`] : []),\n ...grants.map((grant) => `--grant ${formatGrant(grant)}`),\n '--yes',\n ].join(' ')\n}\n"],"names":["Args","Flags","CLIError","exitCodes","SanityCommand","subdebug","getErrorMessage","isProjectRootNotFoundError","input","select","ClientError","MissingOrganizationError","resolveOrganizationId","expiryInDays","parseExpiryDate","DEFAULT_ROLE","findGrantConflict","formatGrant","getProjectTokenMembership","parseGrant","projectGrantKey","toMemberships","validateRole","promptForGrants","promptForProject","listProjects","createOrganizationToken","createToken","getOrganizationFlag","getProjectIdFlag","tokensCreateDebug","EXPIRY_PRESET_DAYS","ORGANIZATION_FORBIDDEN_MESSAGE","CreateTokenCommand","args","label","string","description","required","enableJsonFlag","examples","command","flags","semantics","helpValue","grant","multiple","role","yes","boolean","char","default","hiddenAliases","run","parse","givenLabel","undefined","promptForLabel","trim","error","exit","USAGE_ERROR","flagExpiresAt","parseExpiryFlag","getExpiry","promptForExpiry","flagGrants","map","value","parseGrantFlag","grantConflict","expiresAt","grants","organizationId","roleTitle","isUnattended","length","getFlagGrants","promptForAccess","token","createTokenForGrants","RUNTIME_ERROR","json","logJson","log","id","formatCreateCommand","config","bin","options","projectMembership","resourceId","projectId","roleNames","resolveOrganization","memberships","statusCode","Error","cause","projectIdFlag","resolveProjectId","projectKey","some","key","projectRole","output","roles","name","title","parsed","requestedProjectId","configProjectId","tryGetCliConfig","api","projects","onlyExplicitMembership","organizationOf","find","project","fallback","preferred","organizationProjects","filter","preselectedProjectId","choice","choices","days","slice","String","message","validate","Number","projectOrganizations","configuredOrganizationId","app","flagOrganizationId","organization","unattended","getProjectId","requiredPermissions","permission","quoteShellArg","test","replaceAll","raw","managedBy","resourceType","join"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/tokens/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage, isProjectRootNotFoundError} from '@sanity/cli-core/errors'\nimport {input, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {\n MissingOrganizationError,\n resolveOrganizationId,\n} from '../../actions/context/resolveOrganizationId.js'\nimport {expiryInDays, parseExpiryDate} from '../../actions/tokens/expiry.js'\nimport {\n DEFAULT_ROLE,\n findGrantConflict,\n formatGrant,\n getProjectTokenMembership,\n type Grant,\n parseGrant,\n projectGrantKey,\n toMemberships,\n} from '../../actions/tokens/grants.js'\nimport {type RobotWithToken} from '../../actions/tokens/types.js'\nimport {validateRole} from '../../actions/tokens/validateRole.js'\nimport {promptForGrants} from '../../prompts/promptForGrants.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listProjects} from '../../services/projects.js'\nimport {createOrganizationToken, createToken} from '../../services/tokens.js'\nimport {getOrganizationFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst tokensCreateDebug = subdebug('tokens:create')\n\ntype Access = {\n expiresAt: string | undefined\n grants: Grant[]\n\n organizationId?: string\n roleTitle?: string\n}\n\n// Mirrors the expiry presets offered by sanity.io/manage\nconst EXPIRY_PRESET_DAYS = [30, 60, 90]\n\nconst ORGANIZATION_FORBIDDEN_MESSAGE =\n 'You need permission to create organization tokens. Grant a single project to create a project token instead.'\n\nexport class CreateTokenCommand extends SanityCommand<typeof CreateTokenCommand> {\n static override args = {\n label: Args.string({\n description: 'Label for the new token',\n required: false,\n }),\n }\n\n static override description =\n 'Create a new API token with access to an organization and its projects'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\"',\n description: 'Create a token, choosing its access interactively',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \"CI Token\" --grant context=developer --grant blueprints --grant project:abc123=editor',\n description: 'Create an organization token with Context, Blueprints and project access',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My Token\" --project abc123 --role=editor',\n description: 'Create a project token with editor role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"CI Token\" --role=editor --yes',\n description: 'Create a project token in unattended mode',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"Build Token\" --expires-at 2027-01-01',\n description: 'Create a token that expires on a given date',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"API Token\" --json',\n description: 'Output token information as JSON',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to create token in',\n semantics: 'override',\n }),\n ...getOrganizationFlag({\n description: 'Organization to create an organization token in',\n semantics: 'override',\n }),\n 'expires-at': Flags.string({\n description: 'Date or timestamp the token expires (ISO 8601; tokens never expire by default)',\n helpValue: '2027-01-01',\n }),\n grant: Flags.string({\n description:\n 'Access to grant, as <permission>[=<role>] or project:<projectId>=<role>[,<role>]. Permissions: context, blueprints, media-library, canvas, sdk-apps, access, deploy-studios. Repeatable',\n helpValue: 'context=editor',\n multiple: true,\n }),\n role: Flags.string({\n description: 'Role to assign on the project (defaults to viewer in unattended mode)',\n helpValue: 'viewer',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: 'Skip prompts and use defaults (unattended mode)',\n }),\n }\n\n static override hiddenAliases: string[] = ['tokens:add', 'token:add', 'token:create']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CreateTokenCommand)\n const {label: givenLabel} = args\n\n const label = givenLabel === undefined ? await this.promptForLabel() : givenLabel.trim()\n if (!label) {\n this.error('Token label cannot be empty. Pass a non-empty value as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const flagExpiresAt = this.parseExpiryFlag(flags['expires-at'])\n const getExpiry = async () =>\n flags['expires-at'] === undefined ? this.promptForExpiry() : flagExpiresAt\n const flagGrants = (flags.grant ?? []).map((value) => this.parseGrantFlag(value))\n const grantConflict = findGrantConflict(flagGrants)\n if (grantConflict) {\n this.error(grantConflict, {exit: exitCodes.USAGE_ERROR})\n }\n\n const {expiresAt, grants, organizationId, roleTitle}: Access =\n this.isUnattended() || flagGrants.length > 0 || flags.role !== undefined\n ? {...(await this.getFlagGrants(flagGrants)), expiresAt: await getExpiry()}\n : await this.promptForAccess(getExpiry)\n\n let token: RobotWithToken\n try {\n token = await this.createTokenForGrants({\n expiresAt,\n grants,\n label,\n organizationId,\n })\n } catch (error) {\n if (error instanceof CLIError) throw error\n tokensCreateDebug('Error creating token', error)\n this.error(`Token creation failed:\\n${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (flags.json) {\n this.logJson(token)\n return\n }\n\n this.log('API token created')\n this.log(`Label: ${token.label}`)\n this.log(`ID: ${token.id}`)\n if (roleTitle) {\n this.log(`Role: ${roleTitle}`)\n }\n if (token.expiresAt) {\n this.log(`Expires: ${token.expiresAt}`)\n }\n this.log(`Token: ${token.token}`)\n this.log('')\n this.log(\"Copy the token now. It won't be shown again.\")\n\n if (!this.isUnattended()) {\n this.log('')\n this.log('To create the same token without prompts:')\n this.log(` ${formatCreateCommand(this.config.bin, {expiresAt, grants, label, token})}`)\n }\n }\n\n private async createTokenForGrants(options: {\n expiresAt: string | undefined\n grants: Grant[]\n label: string\n organizationId: string | undefined\n }): Promise<RobotWithToken> {\n const {expiresAt, grants, label} = options\n const projectMembership = getProjectTokenMembership(grants)\n if (projectMembership) {\n const {resourceId: projectId, roleNames} = projectMembership\n return createToken({expiresAt, label, projectId, roleNames})\n }\n\n const organizationId = options.organizationId ?? (await this.resolveOrganization())\n try {\n return await createOrganizationToken({\n expiresAt,\n label,\n memberships: toMemberships(grants, organizationId),\n organizationId,\n })\n } catch (error) {\n if (error instanceof ClientError && error.statusCode === 403) {\n throw new Error(ORGANIZATION_FORBIDDEN_MESSAGE, {cause: error})\n }\n throw error\n }\n }\n\n private async getFlagGrants(grants: Grant[]): Promise<{grants: Grant[]; roleTitle?: string}> {\n const {project: projectIdFlag, role} = this.flags\n if (grants.length > 0 && !projectIdFlag && !role) {\n return {grants}\n }\n\n const projectId = await this.resolveProjectId()\n const projectKey = projectGrantKey(projectId)\n if (grants.some((grant) => grant.key === projectKey)) {\n if (role) {\n this.error(\n `Set the role for project ${projectId} with either --role or --grant ${projectKey}=<role>, not both.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n return {grants}\n }\n\n const projectRole = role ? await validateRole(role, projectId, this.output) : DEFAULT_ROLE\n\n return {\n grants: [...grants, {key: projectKey, roles: [projectRole.name]}],\n roleTitle: grants.length === 0 ? projectRole.title : undefined,\n }\n }\n\n private parseExpiryFlag(value: string | undefined): string | undefined {\n if (value === undefined) {\n return undefined\n }\n\n const parsed = parseExpiryDate(value)\n if ('error' in parsed) {\n this.error(parsed.error, {exit: exitCodes.USAGE_ERROR})\n }\n\n return parsed.expiresAt\n }\n\n private parseGrantFlag(value: string): Grant {\n const parsed = parseGrant(value.trim())\n if ('error' in parsed) {\n this.error(parsed.error, {exit: exitCodes.USAGE_ERROR})\n }\n\n return parsed.grant\n }\n\n private async promptForAccess(getExpiry: () => Promise<string | undefined>): Promise<Access> {\n const requestedProjectId = this.flags.project\n const configProjectId = (await this.tryGetCliConfig()).api?.projectId\n const projects = await listProjects({onlyExplicitMembership: false})\n const organizationOf = (projectId: string | undefined) =>\n projects.find((project) => project.id === projectId)?.organizationId ?? undefined\n const organizationId = await this.resolveOrganization({\n fallback: organizationOf(configProjectId),\n preferred: organizationOf(requestedProjectId),\n })\n const organizationProjects = projects.filter(\n (project) => project.organizationId === organizationId,\n )\n if (requestedProjectId && !organizationProjects.some(({id}) => id === requestedProjectId)) {\n this.error(\n `Project ${requestedProjectId} isn't in organization ${organizationId}, or you don't have access to it.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const expiresAt = await getExpiry()\n const grants = await promptForGrants({\n preselectedProjectId: requestedProjectId,\n projects: organizationProjects,\n })\n return {expiresAt, grants, organizationId}\n }\n\n private async promptForExpiry(): Promise<string | undefined> {\n if (this.isUnattended()) {\n return undefined // Tokens never expire by default\n }\n\n const choice = await select({\n choices: [\n {name: 'Never', value: 'never'},\n ...EXPIRY_PRESET_DAYS.map((days) => ({\n name: `${days} days (${expiryInDays(days).slice(0, 10)})`,\n value: String(days),\n })),\n {name: 'Custom date', value: 'custom'},\n ],\n default: 'never',\n message: 'Token expiry:',\n })\n\n if (choice === 'never') {\n return undefined\n }\n\n if (choice === 'custom') {\n const value = await input({\n message: 'Expiry date (ISO 8601, e.g. 2027-01-01):',\n validate: (input) => {\n const parsed = parseExpiryDate(input.trim())\n return 'error' in parsed ? parsed.error : true\n },\n })\n const parsed = parseExpiryDate(value.trim())\n return 'error' in parsed ? undefined : parsed.expiresAt\n }\n\n return expiryInDays(Number(choice))\n }\n\n private async promptForLabel(): Promise<string> {\n if (this.isUnattended()) {\n this.error('Token label is required. Pass it as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const label = await input({\n message: 'Token label:',\n validate: (value) => {\n if (!value || !value.trim()) {\n return 'Label cannot be empty'\n }\n return true\n },\n })\n\n return label.trim()\n }\n\n private async resolveOrganization(\n projectOrganizations: {fallback?: string; preferred?: string} = {},\n ): Promise<string> {\n const {fallback, preferred} = projectOrganizations\n try {\n return await resolveOrganizationId({\n configuredOrganizationId:\n preferred ?? (await this.tryGetCliConfig()).app?.organizationId ?? fallback,\n flagOrganizationId: this.flags.organization,\n unattended: this.isUnattended(),\n })\n } catch (error) {\n tokensCreateDebug('Error resolving organization', error)\n this.error(getErrorMessage(error), {\n exit:\n error instanceof MissingOrganizationError\n ? exitCodes.USAGE_ERROR\n : exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async resolveProjectId(): Promise<string> {\n if (!this.isUnattended()) {\n return this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.roles'},\n {grant: 'create', permission: 'sanity.project.tokens'},\n ],\n }),\n })\n }\n\n try {\n return await this.getProjectId()\n } catch (error) {\n if (!isProjectRootNotFoundError(error)) throw error\n this.error(\n 'Token access is required. Pass at least one `--grant`, or `--project` to create a project token.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n }\n}\n\nfunction quoteShellArg(value: string): string {\n return /^[\\w./:@=,-]+$/.test(value) ? value : `'${value.replaceAll(\"'\", String.raw`'\\''`)}'`\n}\n\nfunction formatCreateCommand(\n bin: string,\n {\n expiresAt,\n grants,\n label,\n token,\n }: {\n expiresAt: string | undefined\n grants: Grant[]\n label: string\n token: RobotWithToken\n },\n): string {\n const organizationId =\n token.managedBy?.resourceType === 'organization' ? token.managedBy.resourceId : undefined\n return [\n `${bin} tokens create`,\n quoteShellArg(label),\n ...(organizationId ? [`--organization ${organizationId}`] : []),\n ...(expiresAt ? [`--expires-at ${expiresAt}`] : []),\n ...grants.map((grant) => `--grant ${formatGrant(grant)}`),\n '--yes',\n ].join(' ')\n}\n"],"names":["Args","Flags","CLIError","exitCodes","SanityCommand","subdebug","getErrorMessage","isProjectRootNotFoundError","input","select","ClientError","MissingOrganizationError","resolveOrganizationId","expiryInDays","parseExpiryDate","DEFAULT_ROLE","findGrantConflict","formatGrant","getProjectTokenMembership","parseGrant","projectGrantKey","toMemberships","validateRole","promptForGrants","promptForProject","listProjects","createOrganizationToken","createToken","getOrganizationFlag","getProjectIdFlag","tokensCreateDebug","EXPIRY_PRESET_DAYS","ORGANIZATION_FORBIDDEN_MESSAGE","CreateTokenCommand","args","label","string","description","required","enableJsonFlag","examples","command","flags","semantics","helpValue","grant","multiple","role","yes","boolean","char","default","hiddenAliases","run","parse","givenLabel","undefined","promptForLabel","trim","error","exit","USAGE_ERROR","flagExpiresAt","parseExpiryFlag","getExpiry","promptForExpiry","flagGrants","map","value","parseGrantFlag","grantConflict","expiresAt","grants","organizationId","roleTitle","isUnattended","length","getFlagGrants","promptForAccess","token","createTokenForGrants","RUNTIME_ERROR","json","logJson","log","id","formatCreateCommand","config","bin","options","projectMembership","resourceId","projectId","roleNames","resolveOrganization","memberships","statusCode","Error","cause","project","projectIdFlag","resolveProjectId","projectKey","some","key","projectRole","output","roles","name","title","parsed","requestedProjectId","configProjectId","tryGetCliConfig","api","projects","onlyExplicitMembership","organizationOf","find","fallback","preferred","organizationProjects","filter","preselectedProjectId","choice","choices","days","slice","String","message","validate","Number","projectOrganizations","configuredOrganizationId","app","flagOrganizationId","organization","unattended","getProjectId","requiredPermissions","permission","quoteShellArg","test","replaceAll","raw","managedBy","resourceType","join"],"mappings":"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"}
|
|
@@ -39,7 +39,7 @@ export class DeleteTokensCommand extends SanityCommand {
|
|
|
39
39
|
description: 'Delete a specific token without confirmation prompt'
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
|
-
command: '<%= config.bin %> <%= command.id %> --project
|
|
42
|
+
command: '<%= config.bin %> <%= command.id %> --project abc123',
|
|
43
43
|
description: 'Delete a token from a specific project'
|
|
44
44
|
}
|
|
45
45
|
];
|
|
@@ -47,7 +47,7 @@ export class DeleteTokensCommand extends SanityCommand {
|
|
|
47
47
|
...getOrganizationFlag({
|
|
48
48
|
description: 'Delete a token managed by this organization',
|
|
49
49
|
exclusive: [
|
|
50
|
-
'project
|
|
50
|
+
'project'
|
|
51
51
|
],
|
|
52
52
|
semantics: 'specify'
|
|
53
53
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/tokens/delete.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {requiredWhenUnattended} from '@sanity/cli-core/flags'\nimport {confirm, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {formatTokenAccess} from '../../actions/tokens/formatTokenAccess.js'\nimport {type TokenScope} from '../../actions/tokens/types.js'\nimport {deleteToken, getTokens} from '../../services/tokens.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {resolveTokenScope} from '../../util/resolveTokenScope.js'\nimport {getOrganizationFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst deleteTokenDebug = subdebug('tokens:delete')\n\nexport class DeleteTokensCommand extends SanityCommand<typeof DeleteTokensCommand> {\n static override args = {\n tokenId: Args.string({\n description: 'Token ID to delete (will prompt if not provided)',\n required: false,\n }),\n }\n\n static override description = 'Delete an API token from a project or organization'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> --organization org123',\n description: 'Interactively select and delete an organization token',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB --organization org123 --yes',\n description: 'Delete an organization token without confirmation',\n },\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively select and delete a token',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB',\n description: 'Delete a specific token by ID',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB --yes',\n description: 'Delete a specific token without confirmation prompt',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/tokens/delete.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {requiredWhenUnattended} from '@sanity/cli-core/flags'\nimport {confirm, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {formatTokenAccess} from '../../actions/tokens/formatTokenAccess.js'\nimport {type TokenScope} from '../../actions/tokens/types.js'\nimport {deleteToken, getTokens} from '../../services/tokens.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {resolveTokenScope} from '../../util/resolveTokenScope.js'\nimport {getOrganizationFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst deleteTokenDebug = subdebug('tokens:delete')\n\nexport class DeleteTokensCommand extends SanityCommand<typeof DeleteTokensCommand> {\n static override args = {\n tokenId: Args.string({\n description: 'Token ID to delete (will prompt if not provided)',\n required: false,\n }),\n }\n\n static override description = 'Delete an API token from a project or organization'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> --organization org123',\n description: 'Interactively select and delete an organization token',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB --organization org123 --yes',\n description: 'Delete an organization token without confirmation',\n },\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively select and delete a token',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB',\n description: 'Delete a specific token by ID',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB --yes',\n description: 'Delete a specific token without confirmation prompt',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project abc123',\n description: 'Delete a token from a specific project',\n },\n ]\n\n static override flags = {\n ...getOrganizationFlag({\n description: 'Delete a token managed by this organization',\n exclusive: ['project'],\n semantics: 'specify',\n }),\n ...getProjectIdFlag({\n description: 'Project ID to delete token from',\n semantics: 'override',\n }),\n yes: requiredWhenUnattended(\n Flags.boolean({\n char: 'y',\n description: 'Skip confirmation prompt (unattended mode)',\n }),\n ),\n }\n\n static override hiddenAliases: string[] = ['token:delete']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(DeleteTokensCommand)\n\n const skipConfirmation = flags.yes\n const unattended = this.isUnattended()\n const {tokenId: givenTokenId} = args\n\n if (unattended) {\n const errors: string[] = []\n\n if (!givenTokenId) {\n errors.push('Token ID is required. Pass it as the `<tokenId>` argument.')\n }\n if (errors.length > 0) {\n this.error(formatCliErrorMessages(errors), {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n }\n\n const scope = await resolveTokenScope({\n getProjectId: this.getProjectId.bind(this),\n grant: 'delete',\n organization: flags.organization,\n })\n\n const tokenId = givenTokenId || (await this.getTokenIdFromList(scope))\n\n if (!skipConfirmation) {\n const confirmed = await confirm({\n default: false,\n message:\n scope.resourceType === 'organization'\n ? `Delete API token \"${tokenId}\" from organization \"${scope.resourceId}\"? This revokes all of its organization and project access.`\n : `Delete API token \"${tokenId}\"?`,\n })\n\n if (!confirmed) {\n this.log('API token not deleted')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n try {\n await deleteToken(scope, tokenId)\n\n this.log('API token deleted')\n } catch (error) {\n if (error instanceof ClientError && error.response.statusCode === 404) {\n this.error(`Token with ID \"${tokenId}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const err = error as Error\n deleteTokenDebug(`Error deleting token`, err)\n this.error(`Token deletion failed:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private async getTokenIdFromList(scope: TokenScope) {\n let tokens: Awaited<ReturnType<typeof getTokens>>\n try {\n tokens = await getTokens(scope)\n } catch (error) {\n const err = error as Error\n deleteTokenDebug(`Error fetching tokens for ${scope.resourceType} ${scope.resourceId}`, err)\n this.error(\n `Could not list API tokens:\\n${err.message}\\nCheck the ${scope.resourceType} ID and your access permissions, then try again.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n if (tokens.length === 0) {\n this.error(`No API tokens found for this ${scope.resourceType}.`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const choices = tokens.map((token) => {\n const access = formatTokenAccess(token, scope)\n return {name: `${token.label} [${token.id}] (${access})`, value: token.id}\n })\n\n return select({\n choices,\n message: 'Select token to delete:',\n })\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","requiredWhenUnattended","confirm","select","ClientError","formatTokenAccess","deleteToken","getTokens","formatCliErrorMessages","resolveTokenScope","getOrganizationFlag","getProjectIdFlag","deleteTokenDebug","DeleteTokensCommand","args","tokenId","string","description","required","examples","command","flags","exclusive","semantics","yes","boolean","char","hiddenAliases","run","parse","skipConfirmation","unattended","isUnattended","givenTokenId","errors","push","length","error","exit","USAGE_ERROR","scope","getProjectId","bind","grant","organization","getTokenIdFromList","confirmed","default","message","resourceType","resourceId","log","USER_ABORT","response","statusCode","RUNTIME_ERROR","err","tokens","choices","map","token","access","name","label","id","value"],"mappings":"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"}
|
|
@@ -23,7 +23,7 @@ export class TokensListCommand extends SanityCommand {
|
|
|
23
23
|
description: 'List tokens in JSON format'
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
|
-
command: '<%= config.bin %> <%= command.id %> --project
|
|
26
|
+
command: '<%= config.bin %> <%= command.id %> --project abc123',
|
|
27
27
|
description: 'List tokens for a specific project'
|
|
28
28
|
}
|
|
29
29
|
];
|
|
@@ -31,7 +31,7 @@ export class TokensListCommand extends SanityCommand {
|
|
|
31
31
|
...getOrganizationFlag({
|
|
32
32
|
description: 'List tokens managed by this organization',
|
|
33
33
|
exclusive: [
|
|
34
|
-
'project
|
|
34
|
+
'project'
|
|
35
35
|
],
|
|
36
36
|
semantics: 'specify'
|
|
37
37
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/tokens/list.ts"],"sourcesContent":["import {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\n\nimport {formatTokenAccess} from '../../actions/tokens/formatTokenAccess.js'\nimport {type Robot} from '../../actions/tokens/types.js'\nimport {getTokens} from '../../services/tokens.js'\nimport {resolveTokenScope} from '../../util/resolveTokenScope.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getOrganizationFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listTokenDebug = subdebug('tokens:list')\n\nexport class TokensListCommand extends SanityCommand<typeof TokensListCommand> {\n static override description = 'List API tokens for a project or organization'\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> --organization org123',\n description: 'List tokens managed by an organization',\n },\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List tokens for the project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'List tokens in JSON format',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/tokens/list.ts"],"sourcesContent":["import {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\n\nimport {formatTokenAccess} from '../../actions/tokens/formatTokenAccess.js'\nimport {type Robot} from '../../actions/tokens/types.js'\nimport {getTokens} from '../../services/tokens.js'\nimport {resolveTokenScope} from '../../util/resolveTokenScope.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getOrganizationFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listTokenDebug = subdebug('tokens:list')\n\nexport class TokensListCommand extends SanityCommand<typeof TokensListCommand> {\n static override description = 'List API tokens for a project or organization'\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> --organization org123',\n description: 'List tokens managed by an organization',\n },\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List tokens for the project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'List tokens in JSON format',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project abc123',\n description: 'List tokens for a specific project',\n },\n ]\n static override flags = {\n ...getOrganizationFlag({\n description: 'List tokens managed by this organization',\n exclusive: ['project'],\n semantics: 'specify',\n }),\n ...getProjectIdFlag({\n description: 'Project ID to list tokens for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['token:list']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(TokensListCommand)\n const {json} = flags\n const outputJson = json ?? false\n\n const scope = await resolveTokenScope({\n getProjectId: this.getProjectId.bind(this),\n grant: 'read',\n organization: flags.organization,\n })\n\n let tokens: Robot[]\n try {\n tokens = await getTokens(scope)\n } catch (error) {\n const message = getErrorMessage(error)\n listTokenDebug(`Error fetching tokens for ${scope.resourceType} ${scope.resourceId}`, error)\n this.error(`Token list retrieval failed:\\n${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (outputJson) {\n this.logJson(tokens)\n return\n }\n\n if (tokens.length === 0) {\n this.log(`No API tokens found for this ${scope.resourceType}.`)\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'label', title: 'Label'},\n {alignment: 'left', name: 'id', title: 'ID'},\n {\n alignment: 'left',\n name: 'roles',\n title: scope.resourceType === 'organization' ? 'Access' : 'Roles',\n },\n {alignment: 'left', name: 'expires', title: 'Expires'},\n ],\n title: `Found ${tokens.length} API tokens`,\n })\n\n for (const token of tokens) {\n const roles = formatTokenAccess(token, scope)\n\n table.addRow({\n expires: token.expiresAt ? token.expiresAt.slice(0, 10) : 'Never',\n id: token.id,\n label: token.label,\n roles,\n })\n }\n\n this.log(table.render())\n }\n}\n"],"names":["exitCodes","SanityCommand","subdebug","getErrorMessage","formatTokenAccess","getTokens","resolveTokenScope","Table","getOrganizationFlag","getProjectIdFlag","listTokenDebug","TokensListCommand","description","enableJsonFlag","examples","command","flags","exclusive","semantics","hiddenAliases","run","parse","json","outputJson","scope","getProjectId","bind","grant","organization","tokens","error","message","resourceType","resourceId","exit","RUNTIME_ERROR","logJson","length","log","table","columns","alignment","name","title","token","roles","addRow","expires","expiresAt","slice","id","label","render"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AAEvD,SAAQC,iBAAiB,QAAO,4CAA2C;AAE3E,SAAQC,SAAS,QAAO,2BAA0B;AAClD,SAAQC,iBAAiB,QAAO,kCAAiC;AACjE,SAAQC,KAAK,QAAO,gCAA+B;AACnD,SAAQC,mBAAmB,EAAEC,gBAAgB,QAAO,4BAA2B;AAE/E,MAAMC,iBAAiBR,SAAS;AAEhC,OAAO,MAAMS,0BAA0BV;IACrC,OAAgBW,cAAc,gDAA+C;IAC7E,OAAgBC,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IACD,OAAgBI,QAAQ;QACtB,GAAGR,oBAAoB;YACrBI,aAAa;YACbK,WAAW;gBAAC;aAAU;YACtBC,WAAW;QACb,EAAE;QACF,GAAGT,iBAAiB;YAClBG,aAAa;YACbM,WAAW;QACb,EAAE;IACJ,EAAC;IAED,OAAgBC,gBAA0B;QAAC;KAAa,CAAA;IAExD,MAAaC,MAAqB;QAChC,MAAM,EAACJ,KAAK,EAAC,GAAG,MAAM,IAAI,CAACK,KAAK,CAACV;QACjC,MAAM,EAACW,IAAI,EAAC,GAAGN;QACf,MAAMO,aAAaD,QAAQ;QAE3B,MAAME,QAAQ,MAAMlB,kBAAkB;YACpCmB,cAAc,IAAI,CAACA,YAAY,CAACC,IAAI,CAAC,IAAI;YACzCC,OAAO;YACPC,cAAcZ,MAAMY,YAAY;QAClC;QAEA,IAAIC;QACJ,IAAI;YACFA,SAAS,MAAMxB,UAAUmB;QAC3B,EAAE,OAAOM,OAAO;YACd,MAAMC,UAAU5B,gBAAgB2B;YAChCpB,eAAe,CAAC,0BAA0B,EAAEc,MAAMQ,YAAY,CAAC,CAAC,EAAER,MAAMS,UAAU,EAAE,EAAEH;YACtF,IAAI,CAACA,KAAK,CAAC,CAAC,8BAA8B,EAAEC,SAAS,EAAE;gBAACG,MAAMlC,UAAUmC,aAAa;YAAA;QACvF;QAEA,IAAIZ,YAAY;YACd,IAAI,CAACa,OAAO,CAACP;YACb;QACF;QAEA,IAAIA,OAAOQ,MAAM,KAAK,GAAG;YACvB,IAAI,CAACC,GAAG,CAAC,CAAC,6BAA6B,EAAEd,MAAMQ,YAAY,CAAC,CAAC,CAAC;YAC9D;QACF;QAEA,MAAMO,QAAQ,IAAIhC,MAAM;YACtBiC,SAAS;gBACP;oBAACC,WAAW;oBAAQC,MAAM;oBAASC,OAAO;gBAAO;gBACjD;oBAACF,WAAW;oBAAQC,MAAM;oBAAMC,OAAO;gBAAI;gBAC3C;oBACEF,WAAW;oBACXC,MAAM;oBACNC,OAAOnB,MAAMQ,YAAY,KAAK,iBAAiB,WAAW;gBAC5D;gBACA;oBAACS,WAAW;oBAAQC,MAAM;oBAAWC,OAAO;gBAAS;aACtD;YACDA,OAAO,CAAC,MAAM,EAAEd,OAAOQ,MAAM,CAAC,WAAW,CAAC;QAC5C;QAEA,KAAK,MAAMO,SAASf,OAAQ;YAC1B,MAAMgB,QAAQzC,kBAAkBwC,OAAOpB;YAEvCe,MAAMO,MAAM,CAAC;gBACXC,SAASH,MAAMI,SAAS,GAAGJ,MAAMI,SAAS,CAACC,KAAK,CAAC,GAAG,MAAM;gBAC1DC,IAAIN,MAAMM,EAAE;gBACZC,OAAOP,MAAMO,KAAK;gBAClBN;YACF;QACF;QAEA,IAAI,CAACP,GAAG,CAACC,MAAMa,MAAM;IACvB;AACF"}
|
|
@@ -30,7 +30,7 @@ export class UsersInviteCommand extends SanityCommand {
|
|
|
30
30
|
description: 'Send a new user invite to the email "pippi@sanity.io", as administrator'
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
|
-
command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --project
|
|
33
|
+
command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --project abc123',
|
|
34
34
|
description: 'Invite a user to a specific project'
|
|
35
35
|
}
|
|
36
36
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/users/invite.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {requiredWhenUnattended} from '@sanity/cli-core/flags'\nimport {input, select} from '@sanity/cli-core/ux'\n\nimport {type Role} from '../../actions/users/types.js'\nimport {validateEmail} from '../../actions/users/validateEmail.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {getProjectRoles, inviteUser} from '../../services/projects.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst QUOTA_ERROR_MESSAGE =\n 'Project is already at user quota, add billing details to the project in order to allow overage charges.'\n\nconst usersInviteDebug = subdebug('users:invite')\n\nexport class UsersInviteCommand extends SanityCommand<typeof UsersInviteCommand> {\n static override args = {\n email: Args.string({\n description: 'Email address to invite',\n required: false,\n }),\n }\n\n static override description = 'Invite a new user to the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Invite a new user to the project (prompt for details)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io',\n description: 'Send a new user invite to the email \"pippi@sanity.io\", prompt for role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --role administrator',\n description: 'Send a new user invite to the email \"pippi@sanity.io\", as administrator',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --project
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/users/invite.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {requiredWhenUnattended} from '@sanity/cli-core/flags'\nimport {input, select} from '@sanity/cli-core/ux'\n\nimport {type Role} from '../../actions/users/types.js'\nimport {validateEmail} from '../../actions/users/validateEmail.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {getProjectRoles, inviteUser} from '../../services/projects.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst QUOTA_ERROR_MESSAGE =\n 'Project is already at user quota, add billing details to the project in order to allow overage charges.'\n\nconst usersInviteDebug = subdebug('users:invite')\n\nexport class UsersInviteCommand extends SanityCommand<typeof UsersInviteCommand> {\n static override args = {\n email: Args.string({\n description: 'Email address to invite',\n required: false,\n }),\n }\n\n static override description = 'Invite a new user to the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Invite a new user to the project (prompt for details)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io',\n description: 'Send a new user invite to the email \"pippi@sanity.io\", prompt for role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --role administrator',\n description: 'Send a new user invite to the email \"pippi@sanity.io\", as administrator',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --project abc123',\n description: 'Invite a user to a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to invite user to',\n semantics: 'override',\n }),\n role: requiredWhenUnattended(\n Flags.string({\n description: 'Role to invite the user as',\n }),\n ),\n }\n\n static override hiddenAliases: string[] = ['user:invite']\n\n public async run(): Promise<void> {\n const {email: selectedEmail} = this.args\n const {role: selectedRole} = this.flags\n const normalizedEmail = selectedEmail?.trim()\n\n if (this.isUnattended() && !normalizedEmail) {\n this.error('Email address is required. Pass it as the `<email>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (selectedEmail !== undefined) {\n const validation = validateEmail(normalizedEmail ?? '')\n if (validation !== true) this.error(validation, {exit: exitCodes.USAGE_ERROR})\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.roles'},\n {grant: 'invite', permission: 'sanity.project.members'},\n ],\n }),\n })\n\n let roles: Role[]\n try {\n roles = (await getProjectRoles(projectId)).filter((role) => role.appliesToUsers)\n } catch (error) {\n usersInviteDebug('Error fetching roles', error)\n this.error('Error fetching roles', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const email = normalizedEmail || (await this.promptForEmail())\n const roleSelection = selectedRole || (await this.promptForRole(roles))\n const role = roles.find(({name}) => name.toLowerCase() === roleSelection.toLowerCase())\n\n if (!role) {\n this.error(\n `Role name \"${roleSelection}\" not found. Available roles: ${roles.map((r) => r.name).join(', ')}`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n try {\n await inviteUser({email, projectId, role: role.name})\n\n this.log(`Invitation sent to ${email}`)\n } catch (error) {\n usersInviteDebug(`Error inviting user`, error)\n if ((error as Error & {statusCode: number}).statusCode === 402) {\n this.error(QUOTA_ERROR_MESSAGE, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n this.error(`Error inviting user`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private async promptForEmail(): Promise<string> {\n return input({\n message: 'Email to invite:',\n transformer: (val: string) => val.trim(),\n validate: validateEmail,\n })\n }\n\n private async promptForRole(roles: Role[]): Promise<string> {\n return select({\n choices: roles.map((role) => ({\n name: `${role.title} (${role.description || 'No description'})`,\n value: role.name,\n })),\n message: 'Which role should the user have?',\n })\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","requiredWhenUnattended","input","select","validateEmail","promptForProject","getProjectRoles","inviteUser","getProjectIdFlag","QUOTA_ERROR_MESSAGE","usersInviteDebug","UsersInviteCommand","args","email","string","description","required","examples","command","flags","semantics","role","hiddenAliases","run","selectedEmail","selectedRole","normalizedEmail","trim","isUnattended","error","exit","USAGE_ERROR","undefined","validation","projectId","getProjectId","fallback","requiredPermissions","grant","permission","roles","filter","appliesToUsers","RUNTIME_ERROR","promptForEmail","roleSelection","promptForRole","find","name","toLowerCase","map","r","join","log","statusCode","message","transformer","val","validate","choices","title","value"],"mappings":"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"}
|