@sanity/cli 7.11.0 → 7.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/dist/actions/build/buildApp.js +10 -7
  2. package/dist/actions/build/buildApp.js.map +1 -1
  3. package/dist/actions/build/buildStudio.js +9 -6
  4. package/dist/actions/build/buildStudio.js.map +1 -1
  5. package/dist/actions/build/types.js.map +1 -1
  6. package/dist/actions/deploy/deployApp.js +205 -162
  7. package/dist/actions/deploy/deployApp.js.map +1 -1
  8. package/dist/actions/deploy/deployChecks.js +1 -1
  9. package/dist/actions/deploy/deployChecks.js.map +1 -1
  10. package/dist/actions/deploy/deployStudio.js +126 -87
  11. package/dist/actions/deploy/deployStudio.js.map +1 -1
  12. package/dist/actions/deploy/resolveDeployTarget.js +5 -9
  13. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  14. package/dist/actions/dev/devAction.js +1 -2
  15. package/dist/actions/dev/devAction.js.map +1 -1
  16. package/dist/actions/dev/servers/getDevServerConfig.js +8 -2
  17. package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
  18. package/dist/actions/init/bootstrapLocalTemplate.js +18 -9
  19. package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
  20. package/dist/actions/init/createAppCliConfig.js.map +1 -1
  21. package/dist/actions/init/createCliConfig.js.map +1 -1
  22. package/dist/commands/backups/disable.js +4 -4
  23. package/dist/commands/backups/disable.js.map +1 -1
  24. package/dist/commands/backups/download.js +7 -7
  25. package/dist/commands/backups/download.js.map +1 -1
  26. package/dist/commands/backups/enable.js +4 -4
  27. package/dist/commands/backups/enable.js.map +1 -1
  28. package/dist/commands/backups/list.js +7 -7
  29. package/dist/commands/backups/list.js.map +1 -1
  30. package/dist/commands/codemod.js +5 -5
  31. package/dist/commands/codemod.js.map +1 -1
  32. package/dist/commands/cors/add.js +1 -1
  33. package/dist/commands/cors/add.js.map +1 -1
  34. package/dist/commands/cors/delete.js +4 -4
  35. package/dist/commands/cors/delete.js.map +1 -1
  36. package/dist/commands/cors/list.js +2 -2
  37. package/dist/commands/cors/list.js.map +1 -1
  38. package/dist/commands/datasets/alias/create.js +5 -5
  39. package/dist/commands/datasets/alias/create.js.map +1 -1
  40. package/dist/commands/datasets/alias/delete.js +2 -2
  41. package/dist/commands/datasets/alias/delete.js.map +1 -1
  42. package/dist/commands/datasets/alias/link.js +5 -5
  43. package/dist/commands/datasets/alias/link.js.map +1 -1
  44. package/dist/commands/datasets/alias/unlink.js +3 -3
  45. package/dist/commands/datasets/alias/unlink.js.map +1 -1
  46. package/dist/commands/datasets/copy.js +7 -7
  47. package/dist/commands/datasets/copy.js.map +1 -1
  48. package/dist/commands/datasets/create.js +3 -3
  49. package/dist/commands/datasets/create.js.map +1 -1
  50. package/dist/commands/datasets/delete.js +2 -2
  51. package/dist/commands/datasets/delete.js.map +1 -1
  52. package/dist/commands/datasets/embeddings/disable.js +3 -3
  53. package/dist/commands/datasets/embeddings/disable.js.map +1 -1
  54. package/dist/commands/datasets/embeddings/enable.js +5 -5
  55. package/dist/commands/datasets/embeddings/enable.js.map +1 -1
  56. package/dist/commands/datasets/embeddings/status.js +3 -3
  57. package/dist/commands/datasets/embeddings/status.js.map +1 -1
  58. package/dist/commands/datasets/export.js +6 -6
  59. package/dist/commands/datasets/export.js.map +1 -1
  60. package/dist/commands/datasets/import.js +3 -3
  61. package/dist/commands/datasets/import.js.map +1 -1
  62. package/dist/commands/datasets/list.js +2 -2
  63. package/dist/commands/datasets/list.js.map +1 -1
  64. package/dist/commands/datasets/visibility/get.js +2 -2
  65. package/dist/commands/datasets/visibility/get.js.map +1 -1
  66. package/dist/commands/datasets/visibility/set.js +3 -3
  67. package/dist/commands/datasets/visibility/set.js.map +1 -1
  68. package/dist/commands/deploy.js +2 -2
  69. package/dist/commands/deploy.js.map +1 -1
  70. package/dist/commands/dev.js +2 -2
  71. package/dist/commands/dev.js.map +1 -1
  72. package/dist/commands/docs/read.js +1 -1
  73. package/dist/commands/docs/read.js.map +1 -1
  74. package/dist/commands/docs/search.js +3 -3
  75. package/dist/commands/docs/search.js.map +1 -1
  76. package/dist/commands/doctor.js +3 -3
  77. package/dist/commands/doctor.js.map +1 -1
  78. package/dist/commands/documents/create.js +3 -3
  79. package/dist/commands/documents/create.js.map +1 -1
  80. package/dist/commands/documents/delete.js +3 -3
  81. package/dist/commands/documents/delete.js.map +1 -1
  82. package/dist/commands/documents/get.js +2 -2
  83. package/dist/commands/documents/get.js.map +1 -1
  84. package/dist/commands/documents/query.js +2 -2
  85. package/dist/commands/documents/query.js.map +1 -1
  86. package/dist/commands/documents/validate.js +3 -3
  87. package/dist/commands/documents/validate.js.map +1 -1
  88. package/dist/commands/graphql/deploy.js +13 -13
  89. package/dist/commands/graphql/deploy.js.map +1 -1
  90. package/dist/commands/graphql/list.js +2 -2
  91. package/dist/commands/graphql/list.js.map +1 -1
  92. package/dist/commands/graphql/undeploy.js +2 -2
  93. package/dist/commands/graphql/undeploy.js.map +1 -1
  94. package/dist/commands/hooks/attempt.js +2 -2
  95. package/dist/commands/hooks/attempt.js.map +1 -1
  96. package/dist/commands/hooks/create.js +3 -3
  97. package/dist/commands/hooks/create.js.map +1 -1
  98. package/dist/commands/hooks/delete.js +4 -4
  99. package/dist/commands/hooks/delete.js.map +1 -1
  100. package/dist/commands/hooks/list.js +2 -2
  101. package/dist/commands/hooks/list.js.map +1 -1
  102. package/dist/commands/hooks/logs.js +5 -5
  103. package/dist/commands/hooks/logs.js.map +1 -1
  104. package/dist/commands/login.js +2 -2
  105. package/dist/commands/login.js.map +1 -1
  106. package/dist/commands/logout.js +2 -2
  107. package/dist/commands/logout.js.map +1 -1
  108. package/dist/commands/manifest/extract.js +3 -3
  109. package/dist/commands/manifest/extract.js.map +1 -1
  110. package/dist/commands/mcp/configure.js +4 -4
  111. package/dist/commands/mcp/configure.js.map +1 -1
  112. package/dist/commands/media/create-aspect.js +3 -3
  113. package/dist/commands/media/create-aspect.js.map +1 -1
  114. package/dist/commands/media/delete-aspect.js +1 -1
  115. package/dist/commands/media/delete-aspect.js.map +1 -1
  116. package/dist/commands/media/deploy-aspect.js +4 -4
  117. package/dist/commands/media/deploy-aspect.js.map +1 -1
  118. package/dist/commands/media/export.js +7 -7
  119. package/dist/commands/media/export.js.map +1 -1
  120. package/dist/commands/media/import.js +6 -6
  121. package/dist/commands/media/import.js.map +1 -1
  122. package/dist/commands/migrations/create.js +4 -4
  123. package/dist/commands/migrations/create.js.map +1 -1
  124. package/dist/commands/migrations/list.js +2 -2
  125. package/dist/commands/migrations/list.js.map +1 -1
  126. package/dist/commands/migrations/run.js +14 -14
  127. package/dist/commands/migrations/run.js.map +1 -1
  128. package/dist/commands/openapi/get.js +3 -3
  129. package/dist/commands/openapi/get.js.map +1 -1
  130. package/dist/commands/openapi/list.js +2 -2
  131. package/dist/commands/openapi/list.js.map +1 -1
  132. package/dist/commands/organizations/create.js +5 -5
  133. package/dist/commands/organizations/create.js.map +1 -1
  134. package/dist/commands/organizations/delete.js +6 -6
  135. package/dist/commands/organizations/delete.js.map +1 -1
  136. package/dist/commands/organizations/get.js +3 -3
  137. package/dist/commands/organizations/get.js.map +1 -1
  138. package/dist/commands/organizations/list.js +2 -2
  139. package/dist/commands/organizations/list.js.map +1 -1
  140. package/dist/commands/organizations/update.js +6 -6
  141. package/dist/commands/organizations/update.js.map +1 -1
  142. package/dist/commands/preview.js +2 -2
  143. package/dist/commands/preview.js.map +1 -1
  144. package/dist/commands/projects/create.js +2 -2
  145. package/dist/commands/projects/create.js.map +1 -1
  146. package/dist/commands/projects/list.js +2 -2
  147. package/dist/commands/projects/list.js.map +1 -1
  148. package/dist/commands/schemas/delete.js +2 -2
  149. package/dist/commands/schemas/delete.js.map +1 -1
  150. package/dist/commands/schemas/deploy.js +2 -2
  151. package/dist/commands/schemas/deploy.js.map +1 -1
  152. package/dist/commands/schemas/list.js +1 -1
  153. package/dist/commands/schemas/list.js.map +1 -1
  154. package/dist/commands/schemas/validate.js +2 -1
  155. package/dist/commands/schemas/validate.js.map +1 -1
  156. package/dist/commands/skills/install.js +2 -2
  157. package/dist/commands/skills/install.js.map +1 -1
  158. package/dist/commands/telemetry/disable.js +2 -2
  159. package/dist/commands/telemetry/disable.js.map +1 -1
  160. package/dist/commands/telemetry/enable.js +2 -2
  161. package/dist/commands/telemetry/enable.js.map +1 -1
  162. package/dist/commands/tokens/create.js +2 -2
  163. package/dist/commands/tokens/create.js.map +1 -1
  164. package/dist/commands/tokens/delete.js +2 -2
  165. package/dist/commands/tokens/delete.js.map +1 -1
  166. package/dist/commands/tokens/list.js +2 -2
  167. package/dist/commands/tokens/list.js.map +1 -1
  168. package/dist/commands/users/invite.js +3 -3
  169. package/dist/commands/users/invite.js.map +1 -1
  170. package/oclif.manifest.json +331 -331
  171. package/package.json +5 -5
@@ -111,12 +111,12 @@ export class DownloadBackupCommand extends SanityCommand {
111
111
  const message = error instanceof Error ? error.message : String(error);
112
112
  backupDownloadDebug(`Failed to list datasets: ${message}`, error);
113
113
  this.error(`Failed to list datasets: ${message}`, {
114
- exit: 1
114
+ exit: exitCodes.RUNTIME_ERROR
115
115
  });
116
116
  }
117
117
  if (datasets.length === 0) {
118
118
  this.error('No datasets found in this project.', {
119
- exit: 1
119
+ exit: exitCodes.RUNTIME_ERROR
120
120
  });
121
121
  }
122
122
  if (dataset) {
@@ -196,7 +196,7 @@ ${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`, {
196
196
  const message = error instanceof Error ? error.message : String(error);
197
197
  backupDownloadDebug(`Downloading dataset backup failed: ${message}`, error);
198
198
  this.error(`Downloading dataset backup failed: ${message}`, {
199
- exit: 1
199
+ exit: exitCodes.RUNTIME_ERROR
200
200
  });
201
201
  }
202
202
  docOutStream.end();
@@ -216,7 +216,7 @@ ${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`, {
216
216
  const message = err instanceof Error ? err.message : String(err);
217
217
  backupDownloadDebug(`Archiving backup failed: ${message}`, err);
218
218
  this.error(`Archiving backup failed: ${message}`, {
219
- exit: 1
219
+ exit: exitCodes.RUNTIME_ERROR
220
220
  });
221
221
  }
222
222
  progressSpinner.set({
@@ -248,7 +248,7 @@ ${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`, {
248
248
  const err = validateDatasetName(datasetName);
249
249
  if (err) {
250
250
  this.error(err, {
251
- exit: 1
251
+ exit: exitCodes.RUNTIME_ERROR
252
252
  });
253
253
  }
254
254
  const backupId = String(this.flags['backup-id'] || await this.promptForBackupId(projectId, datasetName));
@@ -302,7 +302,7 @@ ${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`, {
302
302
  });
303
303
  if (!response?.backups?.length) {
304
304
  this.error('No backups found', {
305
- exit: 1
305
+ exit: exitCodes.RUNTIME_ERROR
306
306
  });
307
307
  }
308
308
  const backupIdChoices = response.backups.map((backup)=>({
@@ -318,7 +318,7 @@ ${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`, {
318
318
  const message = err instanceof Error ? err.message : String(err);
319
319
  backupDownloadDebug(`Failed to fetch backups for dataset ${datasetName}: ${message}`, err);
320
320
  this.error(`Failed to fetch backups for dataset ${datasetName}: ${message}`, {
321
- exit: 1
321
+ exit: exitCodes.RUNTIME_ERROR
322
322
  });
323
323
  }
324
324
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/backups/download.ts"],"sourcesContent":["import {createWriteStream} from 'node:fs'\nimport {access, mkdir, mkdtemp} from 'node:fs/promises'\nimport {tmpdir} from 'node:os'\nimport path from 'node:path'\nimport {finished} from 'node:stream/promises'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\nimport {boxen, confirm, input, select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport pMap from 'p-map'\nimport prettyMs from 'pretty-ms'\n\nimport {archiveDir} from '../../actions/backup/archiveDir.js'\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {backupDownloadDebug} from '../../actions/backup/backupDownloadDebug.js'\nimport {cleanupTmpDir} from '../../actions/backup/cleanupTmpDir.js'\nimport {downloadAsset} from '../../actions/backup/downloadAsset.js'\nimport {downloadDocument} from '../../actions/backup/downloadDocument.js'\nimport {type File, PaginatedGetBackupStream} from '../../actions/backup/fetchNextBackupPage.js'\nimport {newProgress} from '../../actions/backup/progressSpinner.js'\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type BackupItem, listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {humanFileSize} from '../../util/humanFileSize.js'\nimport {isPathDirName} from '../../util/isPathDirName.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst DEFAULT_DOWNLOAD_CONCURRENCY = 10\nconst MAX_DOWNLOAD_CONCURRENCY = 24\n\ninterface DownloadBackupOptions {\n backupId: string\n concurrency: number\n datasetName: string\n outDir: string\n outFileName: string\n overwrite: boolean\n projectId: string\n}\n\nexport class DownloadBackupCommand extends SanityCommand<typeof DownloadBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to download backup from',\n required: false,\n }),\n }\n\n static override description = 'Download a dataset backup to a local file'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively download a backup',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-1',\n description: 'Download a specific backup for the production dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-2 --out /path/to/file',\n description: 'Download backup to a specific file',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-3 --out /path/to/file --overwrite',\n description: 'Download backup and overwrite existing file',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to download backup from',\n semantics: 'override',\n }),\n 'backup-id': Flags.string({\n description: 'The backup ID to download',\n }),\n concurrency: Flags.integer({\n default: DEFAULT_DOWNLOAD_CONCURRENCY,\n description: `Concurrent number of backup item downloads (max: ${MAX_DOWNLOAD_CONCURRENCY})`,\n }),\n out: Flags.string({\n description: 'The file or directory path the backup should download to',\n }),\n overwrite: Flags.boolean({\n default: false,\n description: 'Allows overwriting of existing backup file',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:download']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(DownloadBackupCommand)\n let {dataset} = args\n\n if (this.isUnattended()) {\n const errors: string[] = []\n\n if (!dataset) {\n errors.push('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.')\n }\n if (!flags['backup-id']) {\n errors.push('Backup ID is required in unattended mode. Pass it with `--backup-id <id>`.')\n }\n\n if (errors.length > 0) {\n this.error(formatCliErrorMessages(errors), {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else {\n dataset = await promptForDataset({allowCreation: false, datasets})\n }\n\n const opts = await this.prepareBackupOptions(projectId, dataset)\n const outFilePath = path.join(opts.outDir, opts.outFileName)\n\n this.log(\n boxen(\n `Downloading backup for:\n\n${styleText('bold', 'projectId')}: ${styleText('cyan', opts.projectId)}\n${styleText('bold', 'dataset')}: ${styleText('cyan', opts.datasetName)}\n${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`,\n {\n borderColor: 'cyan',\n borderStyle: 'round',\n padding: 1,\n },\n ),\n )\n this.log('')\n this.log(`Downloading backup to \"${styleText('cyan', outFilePath)}\"`)\n\n const start = Date.now()\n const progressSpinner = newProgress('Setting up backup environment...')\n\n // Create a unique temporary directory to store files before bundling them into the archive at outputPath.\n // Temporary directories are normally deleted at the end of backup process, any unexpected exit may leave them\n // behind, hence it is important to create a unique directory for each attempt.\n const tmpOutDir = await mkdtemp(path.join(tmpdir(), `sanity-backup-`))\n\n // Create required directories if they don't exist.\n for (const dir of [\n opts.outDir,\n path.join(tmpOutDir, 'images'),\n path.join(tmpOutDir, 'files'),\n ]) {\n await mkdir(dir, {recursive: true})\n }\n\n backupDownloadDebug('Writing to temporary directory %s', tmpOutDir)\n const tmpOutDocumentsFile = path.join(tmpOutDir, 'data.ndjson')\n\n const docOutStream = createWriteStream(tmpOutDocumentsFile)\n\n try {\n const backupFileStream = new PaginatedGetBackupStream(\n opts.projectId,\n opts.datasetName,\n opts.backupId,\n )\n\n const files: File[] = []\n let i = 0\n for await (const file of backupFileStream) {\n files.push(file)\n i++\n progressSpinner.set({\n current: i,\n step: `Reading backup files...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n }\n\n let totalItemsDownloaded = 0\n await pMap(\n files,\n async (file: File) => {\n if (file.type === 'file' || file.type === 'image') {\n await downloadAsset(file.url, file.name, file.type, tmpOutDir)\n } else {\n const doc = await downloadDocument(file.url)\n docOutStream.write(`${doc}\\n`)\n }\n\n totalItemsDownloaded += 1\n progressSpinner.set({\n current: totalItemsDownloaded,\n step: `Downloading documents and assets...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n },\n {concurrency: opts.concurrency},\n )\n } catch (error) {\n progressSpinner.fail()\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Downloading dataset backup failed: ${message}`, error)\n this.error(`Downloading dataset backup failed: ${message}`, {exit: 1})\n }\n\n docOutStream.end()\n await finished(docOutStream)\n\n progressSpinner.set({step: `Archiving files into a tarball...`, update: true})\n try {\n await archiveDir(tmpOutDir, outFilePath, (processedBytes: number) => {\n progressSpinner.update({\n step: `Archiving files into a tarball, ${humanFileSize(processedBytes)} bytes written...`,\n })\n })\n } catch (err) {\n progressSpinner.fail()\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Archiving backup failed: ${message}`, err)\n this.error(`Archiving backup failed: ${message}`, {exit: 1})\n }\n\n progressSpinner.set({\n step: `Cleaning up temporary files at ${styleText('cyan', `${tmpOutDir}`)}`,\n })\n await cleanupTmpDir(tmpOutDir)\n\n progressSpinner.set({\n step: `Backup download complete [${prettyMs(Date.now() - start)}]`,\n })\n progressSpinner.succeed()\n }\n\n private async getOutputPath(defaultOutFileName: string): Promise<string> {\n if (this.flags.out !== undefined) {\n // Rewrite the output path to an absolute path, if it is not already.\n return path.resolve(this.flags.out)\n }\n\n const workDir = process.cwd()\n const defaultPath = path.join(workDir, defaultOutFileName)\n if (this.isUnattended()) {\n return defaultPath\n }\n\n const inputResult = await input({\n default: defaultPath,\n message: 'Output path:',\n })\n return path.resolve(inputResult)\n }\n\n private async prepareBackupOptions(\n projectId: string,\n datasetName: string,\n ): Promise<DownloadBackupOptions> {\n const err = validateDatasetName(datasetName)\n if (err) {\n this.error(err, {exit: 1})\n }\n\n const backupId = String(\n this.flags['backup-id'] || (await this.promptForBackupId(projectId, datasetName)),\n )\n\n if (\n 'concurrency' in this.flags &&\n (this.flags.concurrency < 1 || this.flags.concurrency > MAX_DOWNLOAD_CONCURRENCY)\n ) {\n this.error(`\\`--concurrency\\` must be between 1 and ${MAX_DOWNLOAD_CONCURRENCY}.`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const defaultOutFileName = `${datasetName}-backup-${backupId}.tar.gz`\n let out = await this.getOutputPath(defaultOutFileName)\n\n // If path is a directory name, then add a default file name to the path.\n if (isPathDirName(out)) {\n out = path.join(out, defaultOutFileName)\n }\n\n const exists = await access(out).then(\n () => true,\n () => false,\n )\n // If the file already exists, ask for confirmation if it should be overwritten.\n if (!this.flags.overwrite && exists) {\n if (this.isUnattended()) {\n this.error(`File \"${out}\" already exists. Pass \\`--overwrite\\` to replace it.`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const shouldOverwrite = await confirm({\n default: false,\n message: `File \"${out}\" already exists, would you like to overwrite it?`,\n })\n\n // If the user does not want to overwrite the file, cancel the operation.\n if (!shouldOverwrite) {\n this.error('Operation cancelled.', {exit: exitCodes.USER_ABORT})\n }\n }\n\n return {\n backupId,\n concurrency: this.flags.concurrency || DEFAULT_DOWNLOAD_CONCURRENCY,\n datasetName,\n outDir: path.dirname(out),\n outFileName: path.basename(out),\n overwrite: this.flags.overwrite,\n projectId,\n }\n }\n\n private async promptForBackupId(projectId: string, datasetName: string): Promise<string> {\n const maxBackupIdsShown = 100\n\n try {\n const response = await listBackups({\n datasetName,\n limit: maxBackupIdsShown,\n projectId,\n })\n\n if (!response?.backups?.length) {\n this.error('No backups found', {exit: 1})\n }\n\n const backupIdChoices = response.backups.map((backup: BackupItem) => ({\n name: backup.id,\n value: backup.id,\n }))\n\n const hint =\n backupIdChoices.length === maxBackupIdsShown\n ? ` (only last ${maxBackupIdsShown} shown)`\n : ''\n\n return select({\n choices: backupIdChoices,\n message: `Select backup ID to use${hint}`,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Failed to fetch backups for dataset ${datasetName}: ${message}`, err)\n this.error(`Failed to fetch backups for dataset ${datasetName}: ${message}`, {exit: 1})\n }\n }\n}\n"],"names":["createWriteStream","access","mkdir","mkdtemp","tmpdir","path","finished","styleText","Args","Flags","exitCodes","SanityCommand","boxen","confirm","input","select","pMap","prettyMs","archiveDir","assertDatasetExists","backupDownloadDebug","cleanupTmpDir","downloadAsset","downloadDocument","PaginatedGetBackupStream","newProgress","validateDatasetName","promptForDataset","promptForProject","listBackups","listDatasets","formatCliErrorMessages","humanFileSize","isPathDirName","getProjectIdFlag","DEFAULT_DOWNLOAD_CONCURRENCY","MAX_DOWNLOAD_CONCURRENCY","DownloadBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","concurrency","integer","default","out","overwrite","boolean","hiddenAliases","run","parse","isUnattended","errors","push","length","error","exit","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","message","Error","String","output","allowCreation","opts","prepareBackupOptions","outFilePath","join","outDir","outFileName","log","datasetName","backupId","borderColor","borderStyle","padding","start","Date","now","progressSpinner","tmpOutDir","dir","recursive","tmpOutDocumentsFile","docOutStream","backupFileStream","files","i","file","set","current","step","total","totalFiles","update","totalItemsDownloaded","type","url","name","doc","write","fail","end","processedBytes","err","succeed","getOutputPath","defaultOutFileName","undefined","resolve","workDir","process","cwd","defaultPath","inputResult","promptForBackupId","exists","then","shouldOverwrite","USER_ABORT","dirname","basename","maxBackupIdsShown","response","limit","backups","backupIdChoices","map","backup","id","value","hint","choices"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/backups/download.ts"],"sourcesContent":["import {createWriteStream} from 'node:fs'\nimport {access, mkdir, mkdtemp} from 'node:fs/promises'\nimport {tmpdir} from 'node:os'\nimport path from 'node:path'\nimport {finished} from 'node:stream/promises'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\nimport {boxen, confirm, input, select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport pMap from 'p-map'\nimport prettyMs from 'pretty-ms'\n\nimport {archiveDir} from '../../actions/backup/archiveDir.js'\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {backupDownloadDebug} from '../../actions/backup/backupDownloadDebug.js'\nimport {cleanupTmpDir} from '../../actions/backup/cleanupTmpDir.js'\nimport {downloadAsset} from '../../actions/backup/downloadAsset.js'\nimport {downloadDocument} from '../../actions/backup/downloadDocument.js'\nimport {type File, PaginatedGetBackupStream} from '../../actions/backup/fetchNextBackupPage.js'\nimport {newProgress} from '../../actions/backup/progressSpinner.js'\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type BackupItem, listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {humanFileSize} from '../../util/humanFileSize.js'\nimport {isPathDirName} from '../../util/isPathDirName.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst DEFAULT_DOWNLOAD_CONCURRENCY = 10\nconst MAX_DOWNLOAD_CONCURRENCY = 24\n\ninterface DownloadBackupOptions {\n backupId: string\n concurrency: number\n datasetName: string\n outDir: string\n outFileName: string\n overwrite: boolean\n projectId: string\n}\n\nexport class DownloadBackupCommand extends SanityCommand<typeof DownloadBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to download backup from',\n required: false,\n }),\n }\n\n static override description = 'Download a dataset backup to a local file'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively download a backup',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-1',\n description: 'Download a specific backup for the production dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-2 --out /path/to/file',\n description: 'Download backup to a specific file',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-3 --out /path/to/file --overwrite',\n description: 'Download backup and overwrite existing file',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to download backup from',\n semantics: 'override',\n }),\n 'backup-id': Flags.string({\n description: 'The backup ID to download',\n }),\n concurrency: Flags.integer({\n default: DEFAULT_DOWNLOAD_CONCURRENCY,\n description: `Concurrent number of backup item downloads (max: ${MAX_DOWNLOAD_CONCURRENCY})`,\n }),\n out: Flags.string({\n description: 'The file or directory path the backup should download to',\n }),\n overwrite: Flags.boolean({\n default: false,\n description: 'Allows overwriting of existing backup file',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:download']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(DownloadBackupCommand)\n let {dataset} = args\n\n if (this.isUnattended()) {\n const errors: string[] = []\n\n if (!dataset) {\n errors.push('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.')\n }\n if (!flags['backup-id']) {\n errors.push('Backup ID is required in unattended mode. Pass it with `--backup-id <id>`.')\n }\n\n if (errors.length > 0) {\n this.error(formatCliErrorMessages(errors), {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else {\n dataset = await promptForDataset({allowCreation: false, datasets})\n }\n\n const opts = await this.prepareBackupOptions(projectId, dataset)\n const outFilePath = path.join(opts.outDir, opts.outFileName)\n\n this.log(\n boxen(\n `Downloading backup for:\n\n${styleText('bold', 'projectId')}: ${styleText('cyan', opts.projectId)}\n${styleText('bold', 'dataset')}: ${styleText('cyan', opts.datasetName)}\n${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`,\n {\n borderColor: 'cyan',\n borderStyle: 'round',\n padding: 1,\n },\n ),\n )\n this.log('')\n this.log(`Downloading backup to \"${styleText('cyan', outFilePath)}\"`)\n\n const start = Date.now()\n const progressSpinner = newProgress('Setting up backup environment...')\n\n // Create a unique temporary directory to store files before bundling them into the archive at outputPath.\n // Temporary directories are normally deleted at the end of backup process, any unexpected exit may leave them\n // behind, hence it is important to create a unique directory for each attempt.\n const tmpOutDir = await mkdtemp(path.join(tmpdir(), `sanity-backup-`))\n\n // Create required directories if they don't exist.\n for (const dir of [\n opts.outDir,\n path.join(tmpOutDir, 'images'),\n path.join(tmpOutDir, 'files'),\n ]) {\n await mkdir(dir, {recursive: true})\n }\n\n backupDownloadDebug('Writing to temporary directory %s', tmpOutDir)\n const tmpOutDocumentsFile = path.join(tmpOutDir, 'data.ndjson')\n\n const docOutStream = createWriteStream(tmpOutDocumentsFile)\n\n try {\n const backupFileStream = new PaginatedGetBackupStream(\n opts.projectId,\n opts.datasetName,\n opts.backupId,\n )\n\n const files: File[] = []\n let i = 0\n for await (const file of backupFileStream) {\n files.push(file)\n i++\n progressSpinner.set({\n current: i,\n step: `Reading backup files...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n }\n\n let totalItemsDownloaded = 0\n await pMap(\n files,\n async (file: File) => {\n if (file.type === 'file' || file.type === 'image') {\n await downloadAsset(file.url, file.name, file.type, tmpOutDir)\n } else {\n const doc = await downloadDocument(file.url)\n docOutStream.write(`${doc}\\n`)\n }\n\n totalItemsDownloaded += 1\n progressSpinner.set({\n current: totalItemsDownloaded,\n step: `Downloading documents and assets...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n },\n {concurrency: opts.concurrency},\n )\n } catch (error) {\n progressSpinner.fail()\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Downloading dataset backup failed: ${message}`, error)\n this.error(`Downloading dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n docOutStream.end()\n await finished(docOutStream)\n\n progressSpinner.set({step: `Archiving files into a tarball...`, update: true})\n try {\n await archiveDir(tmpOutDir, outFilePath, (processedBytes: number) => {\n progressSpinner.update({\n step: `Archiving files into a tarball, ${humanFileSize(processedBytes)} bytes written...`,\n })\n })\n } catch (err) {\n progressSpinner.fail()\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Archiving backup failed: ${message}`, err)\n this.error(`Archiving backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n progressSpinner.set({\n step: `Cleaning up temporary files at ${styleText('cyan', `${tmpOutDir}`)}`,\n })\n await cleanupTmpDir(tmpOutDir)\n\n progressSpinner.set({\n step: `Backup download complete [${prettyMs(Date.now() - start)}]`,\n })\n progressSpinner.succeed()\n }\n\n private async getOutputPath(defaultOutFileName: string): Promise<string> {\n if (this.flags.out !== undefined) {\n // Rewrite the output path to an absolute path, if it is not already.\n return path.resolve(this.flags.out)\n }\n\n const workDir = process.cwd()\n const defaultPath = path.join(workDir, defaultOutFileName)\n if (this.isUnattended()) {\n return defaultPath\n }\n\n const inputResult = await input({\n default: defaultPath,\n message: 'Output path:',\n })\n return path.resolve(inputResult)\n }\n\n private async prepareBackupOptions(\n projectId: string,\n datasetName: string,\n ): Promise<DownloadBackupOptions> {\n const err = validateDatasetName(datasetName)\n if (err) {\n this.error(err, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const backupId = String(\n this.flags['backup-id'] || (await this.promptForBackupId(projectId, datasetName)),\n )\n\n if (\n 'concurrency' in this.flags &&\n (this.flags.concurrency < 1 || this.flags.concurrency > MAX_DOWNLOAD_CONCURRENCY)\n ) {\n this.error(`\\`--concurrency\\` must be between 1 and ${MAX_DOWNLOAD_CONCURRENCY}.`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const defaultOutFileName = `${datasetName}-backup-${backupId}.tar.gz`\n let out = await this.getOutputPath(defaultOutFileName)\n\n // If path is a directory name, then add a default file name to the path.\n if (isPathDirName(out)) {\n out = path.join(out, defaultOutFileName)\n }\n\n const exists = await access(out).then(\n () => true,\n () => false,\n )\n // If the file already exists, ask for confirmation if it should be overwritten.\n if (!this.flags.overwrite && exists) {\n if (this.isUnattended()) {\n this.error(`File \"${out}\" already exists. Pass \\`--overwrite\\` to replace it.`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const shouldOverwrite = await confirm({\n default: false,\n message: `File \"${out}\" already exists, would you like to overwrite it?`,\n })\n\n // If the user does not want to overwrite the file, cancel the operation.\n if (!shouldOverwrite) {\n this.error('Operation cancelled.', {exit: exitCodes.USER_ABORT})\n }\n }\n\n return {\n backupId,\n concurrency: this.flags.concurrency || DEFAULT_DOWNLOAD_CONCURRENCY,\n datasetName,\n outDir: path.dirname(out),\n outFileName: path.basename(out),\n overwrite: this.flags.overwrite,\n projectId,\n }\n }\n\n private async promptForBackupId(projectId: string, datasetName: string): Promise<string> {\n const maxBackupIdsShown = 100\n\n try {\n const response = await listBackups({\n datasetName,\n limit: maxBackupIdsShown,\n projectId,\n })\n\n if (!response?.backups?.length) {\n this.error('No backups found', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const backupIdChoices = response.backups.map((backup: BackupItem) => ({\n name: backup.id,\n value: backup.id,\n }))\n\n const hint =\n backupIdChoices.length === maxBackupIdsShown\n ? ` (only last ${maxBackupIdsShown} shown)`\n : ''\n\n return select({\n choices: backupIdChoices,\n message: `Select backup ID to use${hint}`,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Failed to fetch backups for dataset ${datasetName}: ${message}`, err)\n this.error(`Failed to fetch backups for dataset ${datasetName}: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["createWriteStream","access","mkdir","mkdtemp","tmpdir","path","finished","styleText","Args","Flags","exitCodes","SanityCommand","boxen","confirm","input","select","pMap","prettyMs","archiveDir","assertDatasetExists","backupDownloadDebug","cleanupTmpDir","downloadAsset","downloadDocument","PaginatedGetBackupStream","newProgress","validateDatasetName","promptForDataset","promptForProject","listBackups","listDatasets","formatCliErrorMessages","humanFileSize","isPathDirName","getProjectIdFlag","DEFAULT_DOWNLOAD_CONCURRENCY","MAX_DOWNLOAD_CONCURRENCY","DownloadBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","concurrency","integer","default","out","overwrite","boolean","hiddenAliases","run","parse","isUnattended","errors","push","length","error","exit","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","message","Error","String","RUNTIME_ERROR","output","allowCreation","opts","prepareBackupOptions","outFilePath","join","outDir","outFileName","log","datasetName","backupId","borderColor","borderStyle","padding","start","Date","now","progressSpinner","tmpOutDir","dir","recursive","tmpOutDocumentsFile","docOutStream","backupFileStream","files","i","file","set","current","step","total","totalFiles","update","totalItemsDownloaded","type","url","name","doc","write","fail","end","processedBytes","err","succeed","getOutputPath","defaultOutFileName","undefined","resolve","workDir","process","cwd","defaultPath","inputResult","promptForBackupId","exists","then","shouldOverwrite","USER_ABORT","dirname","basename","maxBackupIdsShown","response","limit","backups","backupIdChoices","map","backup","id","value","hint","choices"],"mappings":"AAAA,SAAQA,iBAAiB,QAAO,UAAS;AACzC,SAAQC,MAAM,EAAEC,KAAK,EAAEC,OAAO,QAAO,mBAAkB;AACvD,SAAQC,MAAM,QAAO,UAAS;AAC9B,OAAOC,UAAU,YAAW;AAC5B,SAAQC,QAAQ,QAAO,uBAAsB;AAC7C,SAAQC,SAAS,QAAO,YAAW;AAEnC,SAAQC,IAAI,EAAEC,KAAK,QAAO,cAAa;AACvC,SAAQC,SAAS,EAAEC,aAAa,QAAO,mBAAkB;AACzD,SAAQC,KAAK,EAAEC,OAAO,EAAEC,KAAK,EAAEC,MAAM,QAAO,sBAAqB;AAEjE,OAAOC,UAAU,QAAO;AACxB,OAAOC,cAAc,YAAW;AAEhC,SAAQC,UAAU,QAAO,qCAAoC;AAC7D,SAAQC,mBAAmB,QAAO,6CAA4C;AAC9E,SAAQC,mBAAmB,QAAO,8CAA6C;AAC/E,SAAQC,aAAa,QAAO,wCAAuC;AACnE,SAAQC,aAAa,QAAO,wCAAuC;AACnE,SAAQC,gBAAgB,QAAO,2CAA0C;AACzE,SAAmBC,wBAAwB,QAAO,8CAA6C;AAC/F,SAAQC,WAAW,QAAO,0CAAyC;AACnE,SAAQC,mBAAmB,QAAO,+CAA8C;AAChF,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAyBC,WAAW,QAAO,2BAA0B;AACrE,SAAQC,YAAY,QAAO,6BAA4B;AACvD,SAAQC,sBAAsB,QAAO,uCAAsC;AAC3E,SAAQC,aAAa,QAAO,8BAA6B;AACzD,SAAQC,aAAa,QAAO,8BAA6B;AACzD,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,+BAA+B;AACrC,MAAMC,2BAA2B;AAYjC,OAAO,MAAMC,8BAA8B1B;IACzC,OAAgB2B,OAAO;QACrBC,SAAS/B,KAAKgC,MAAM,CAAC;YACnBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,4CAA2C;IAEzE,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SACE;YACFH,aAAa;QACf;QACA;YACEG,SACE;YACFH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtB,GAAGX,iBAAiB;YAClBO,aAAa;YACbK,WAAW;QACb,EAAE;QACF,aAAarC,MAAM+B,MAAM,CAAC;YACxBC,aAAa;QACf;QACAM,aAAatC,MAAMuC,OAAO,CAAC;YACzBC,SAASd;YACTM,aAAa,CAAC,iDAAiD,EAAEL,yBAAyB,CAAC,CAAC;QAC9F;QACAc,KAAKzC,MAAM+B,MAAM,CAAC;YAChBC,aAAa;QACf;QACAU,WAAW1C,MAAM2C,OAAO,CAAC;YACvBH,SAAS;YACTR,aAAa;QACf;IACF,EAAC;IAED,OAAgBY,gBAA0B;QAAC;KAAkB,CAAA;IAE7D,MAAaC,MAAqB;QAChC,MAAM,EAAChB,IAAI,EAAEO,KAAK,EAAC,GAAG,MAAM,IAAI,CAACU,KAAK,CAAClB;QACvC,IAAI,EAACE,OAAO,EAAC,GAAGD;QAEhB,IAAI,IAAI,CAACkB,YAAY,IAAI;YACvB,MAAMC,SAAmB,EAAE;YAE3B,IAAI,CAAClB,SAAS;gBACZkB,OAAOC,IAAI,CAAC;YACd;YACA,IAAI,CAACb,KAAK,CAAC,YAAY,EAAE;gBACvBY,OAAOC,IAAI,CAAC;YACd;YAEA,IAAID,OAAOE,MAAM,GAAG,GAAG;gBACrB,IAAI,CAACC,KAAK,CAAC7B,uBAAuB0B,SAAS;oBAACI,MAAMnD,UAAUoD,WAAW;gBAAA;YACzE;QACF;QAEA,MAAMC,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACRrC,iBAAiB;oBACfsC,qBAAqB;wBAAC;4BAACC,OAAO;4BAAQC,YAAY;wBAAyB;qBAAE;gBAC/E;QACJ;QAEA,IAAIC;QAEJ,IAAI;YACFA,WAAW,MAAMvC,aAAaiC;QAChC,EAAE,OAAOH,OAAO;YACd,MAAMU,UAAUV,iBAAiBW,QAAQX,MAAMU,OAAO,GAAGE,OAAOZ;YAChExC,oBAAoB,CAAC,yBAAyB,EAAEkD,SAAS,EAAEV;YAC3D,IAAI,CAACA,KAAK,CAAC,CAAC,yBAAyB,EAAEU,SAAS,EAAE;gBAACT,MAAMnD,UAAU+D,aAAa;YAAA;QAClF;QAEA,IAAIJ,SAASV,MAAM,KAAK,GAAG;YACzB,IAAI,CAACC,KAAK,CAAC,sCAAsC;gBAACC,MAAMnD,UAAU+D,aAAa;YAAA;QACjF;QAEA,IAAIlC,SAAS;YACXpB,oBAAoBkD,UAAU9B,SAAS,IAAI,CAACmC,MAAM;QACpD,OAAO;YACLnC,UAAU,MAAMZ,iBAAiB;gBAACgD,eAAe;gBAAON;YAAQ;QAClE;QAEA,MAAMO,OAAO,MAAM,IAAI,CAACC,oBAAoB,CAACd,WAAWxB;QACxD,MAAMuC,cAAczE,KAAK0E,IAAI,CAACH,KAAKI,MAAM,EAAEJ,KAAKK,WAAW;QAE3D,IAAI,CAACC,GAAG,CACNtE,MACE,CAAC;;AAET,EAAEL,UAAU,QAAQ,aAAa,EAAE,EAAEA,UAAU,QAAQqE,KAAKb,SAAS,EAAE;AACvE,EAAExD,UAAU,QAAQ,WAAW,EAAE,EAAEA,UAAU,QAAQqE,KAAKO,WAAW,EAAE;AACvE,EAAE5E,UAAU,QAAQ,YAAY,EAAE,EAAEA,UAAU,QAAQqE,KAAKQ,QAAQ,GAAG,EAC9D;YACEC,aAAa;YACbC,aAAa;YACbC,SAAS;QACX;QAGJ,IAAI,CAACL,GAAG,CAAC;QACT,IAAI,CAACA,GAAG,CAAC,CAAC,uBAAuB,EAAE3E,UAAU,QAAQuE,aAAa,CAAC,CAAC;QAEpE,MAAMU,QAAQC,KAAKC,GAAG;QACtB,MAAMC,kBAAkBlE,YAAY;QAEpC,0GAA0G;QAC1G,8GAA8G;QAC9G,+EAA+E;QAC/E,MAAMmE,YAAY,MAAMzF,QAAQE,KAAK0E,IAAI,CAAC3E,UAAU,CAAC,cAAc,CAAC;QAEpE,mDAAmD;QACnD,KAAK,MAAMyF,OAAO;YAChBjB,KAAKI,MAAM;YACX3E,KAAK0E,IAAI,CAACa,WAAW;YACrBvF,KAAK0E,IAAI,CAACa,WAAW;SACtB,CAAE;YACD,MAAM1F,MAAM2F,KAAK;gBAACC,WAAW;YAAI;QACnC;QAEA1E,oBAAoB,qCAAqCwE;QACzD,MAAMG,sBAAsB1F,KAAK0E,IAAI,CAACa,WAAW;QAEjD,MAAMI,eAAehG,kBAAkB+F;QAEvC,IAAI;YACF,MAAME,mBAAmB,IAAIzE,yBAC3BoD,KAAKb,SAAS,EACda,KAAKO,WAAW,EAChBP,KAAKQ,QAAQ;YAGf,MAAMc,QAAgB,EAAE;YACxB,IAAIC,IAAI;YACR,WAAW,MAAMC,QAAQH,iBAAkB;gBACzCC,MAAMxC,IAAI,CAAC0C;gBACXD;gBACAR,gBAAgBU,GAAG,CAAC;oBAClBC,SAASH;oBACTI,MAAM,CAAC,uBAAuB,CAAC;oBAC/BC,OAAOP,iBAAiBQ,UAAU;oBAClCC,QAAQ;gBACV;YACF;YAEA,IAAIC,uBAAuB;YAC3B,MAAM3F,KACJkF,OACA,OAAOE;gBACL,IAAIA,KAAKQ,IAAI,KAAK,UAAUR,KAAKQ,IAAI,KAAK,SAAS;oBACjD,MAAMtF,cAAc8E,KAAKS,GAAG,EAAET,KAAKU,IAAI,EAAEV,KAAKQ,IAAI,EAAEhB;gBACtD,OAAO;oBACL,MAAMmB,MAAM,MAAMxF,iBAAiB6E,KAAKS,GAAG;oBAC3Cb,aAAagB,KAAK,CAAC,GAAGD,IAAI,EAAE,CAAC;gBAC/B;gBAEAJ,wBAAwB;gBACxBhB,gBAAgBU,GAAG,CAAC;oBAClBC,SAASK;oBACTJ,MAAM,CAAC,mCAAmC,CAAC;oBAC3CC,OAAOP,iBAAiBQ,UAAU;oBAClCC,QAAQ;gBACV;YACF,GACA;gBAAC3D,aAAa6B,KAAK7B,WAAW;YAAA;QAElC,EAAE,OAAOa,OAAO;YACd+B,gBAAgBsB,IAAI;YACpB,MAAM3C,UAAUV,iBAAiBW,QAAQX,MAAMU,OAAO,GAAGE,OAAOZ;YAChExC,oBAAoB,CAAC,mCAAmC,EAAEkD,SAAS,EAAEV;YACrE,IAAI,CAACA,KAAK,CAAC,CAAC,mCAAmC,EAAEU,SAAS,EAAE;gBAACT,MAAMnD,UAAU+D,aAAa;YAAA;QAC5F;QAEAuB,aAAakB,GAAG;QAChB,MAAM5G,SAAS0F;QAEfL,gBAAgBU,GAAG,CAAC;YAACE,MAAM,CAAC,iCAAiC,CAAC;YAAEG,QAAQ;QAAI;QAC5E,IAAI;YACF,MAAMxF,WAAW0E,WAAWd,aAAa,CAACqC;gBACxCxB,gBAAgBe,MAAM,CAAC;oBACrBH,MAAM,CAAC,gCAAgC,EAAEvE,cAAcmF,gBAAgB,iBAAiB,CAAC;gBAC3F;YACF;QACF,EAAE,OAAOC,KAAK;YACZzB,gBAAgBsB,IAAI;YACpB,MAAM3C,UAAU8C,eAAe7C,QAAQ6C,IAAI9C,OAAO,GAAGE,OAAO4C;YAC5DhG,oBAAoB,CAAC,yBAAyB,EAAEkD,SAAS,EAAE8C;YAC3D,IAAI,CAACxD,KAAK,CAAC,CAAC,yBAAyB,EAAEU,SAAS,EAAE;gBAACT,MAAMnD,UAAU+D,aAAa;YAAA;QAClF;QAEAkB,gBAAgBU,GAAG,CAAC;YAClBE,MAAM,CAAC,+BAA+B,EAAEhG,UAAU,QAAQ,GAAGqF,WAAW,GAAG;QAC7E;QACA,MAAMvE,cAAcuE;QAEpBD,gBAAgBU,GAAG,CAAC;YAClBE,MAAM,CAAC,0BAA0B,EAAEtF,SAASwE,KAAKC,GAAG,KAAKF,OAAO,CAAC,CAAC;QACpE;QACAG,gBAAgB0B,OAAO;IACzB;IAEA,MAAcC,cAAcC,kBAA0B,EAAmB;QACvE,IAAI,IAAI,CAAC1E,KAAK,CAACK,GAAG,KAAKsE,WAAW;YAChC,qEAAqE;YACrE,OAAOnH,KAAKoH,OAAO,CAAC,IAAI,CAAC5E,KAAK,CAACK,GAAG;QACpC;QAEA,MAAMwE,UAAUC,QAAQC,GAAG;QAC3B,MAAMC,cAAcxH,KAAK0E,IAAI,CAAC2C,SAASH;QACvC,IAAI,IAAI,CAAC/D,YAAY,IAAI;YACvB,OAAOqE;QACT;QAEA,MAAMC,cAAc,MAAMhH,MAAM;YAC9BmC,SAAS4E;YACTvD,SAAS;QACX;QACA,OAAOjE,KAAKoH,OAAO,CAACK;IACtB;IAEA,MAAcjD,qBACZd,SAAiB,EACjBoB,WAAmB,EACa;QAChC,MAAMiC,MAAM1F,oBAAoByD;QAChC,IAAIiC,KAAK;YACP,IAAI,CAACxD,KAAK,CAACwD,KAAK;gBAACvD,MAAMnD,UAAU+D,aAAa;YAAA;QAChD;QAEA,MAAMW,WAAWZ,OACf,IAAI,CAAC3B,KAAK,CAAC,YAAY,IAAK,MAAM,IAAI,CAACkF,iBAAiB,CAAChE,WAAWoB;QAGtE,IACE,iBAAiB,IAAI,CAACtC,KAAK,IAC1B,CAAA,IAAI,CAACA,KAAK,CAACE,WAAW,GAAG,KAAK,IAAI,CAACF,KAAK,CAACE,WAAW,GAAGX,wBAAuB,GAC/E;YACA,IAAI,CAACwB,KAAK,CAAC,CAAC,wCAAwC,EAAExB,yBAAyB,CAAC,CAAC,EAAE;gBACjFyB,MAAMnD,UAAUoD,WAAW;YAC7B;QACF;QAEA,MAAMyD,qBAAqB,GAAGpC,YAAY,QAAQ,EAAEC,SAAS,OAAO,CAAC;QACrE,IAAIlC,MAAM,MAAM,IAAI,CAACoE,aAAa,CAACC;QAEnC,yEAAyE;QACzE,IAAItF,cAAciB,MAAM;YACtBA,MAAM7C,KAAK0E,IAAI,CAAC7B,KAAKqE;QACvB;QAEA,MAAMS,SAAS,MAAM/H,OAAOiD,KAAK+E,IAAI,CACnC,IAAM,MACN,IAAM;QAER,gFAAgF;QAChF,IAAI,CAAC,IAAI,CAACpF,KAAK,CAACM,SAAS,IAAI6E,QAAQ;YACnC,IAAI,IAAI,CAACxE,YAAY,IAAI;gBACvB,IAAI,CAACI,KAAK,CAAC,CAAC,MAAM,EAAEV,IAAI,qDAAqD,CAAC,EAAE;oBAC9EW,MAAMnD,UAAUoD,WAAW;gBAC7B;YACF;YACA,MAAMoE,kBAAkB,MAAMrH,QAAQ;gBACpCoC,SAAS;gBACTqB,SAAS,CAAC,MAAM,EAAEpB,IAAI,iDAAiD,CAAC;YAC1E;YAEA,yEAAyE;YACzE,IAAI,CAACgF,iBAAiB;gBACpB,IAAI,CAACtE,KAAK,CAAC,wBAAwB;oBAACC,MAAMnD,UAAUyH,UAAU;gBAAA;YAChE;QACF;QAEA,OAAO;YACL/C;YACArC,aAAa,IAAI,CAACF,KAAK,CAACE,WAAW,IAAIZ;YACvCgD;YACAH,QAAQ3E,KAAK+H,OAAO,CAAClF;YACrB+B,aAAa5E,KAAKgI,QAAQ,CAACnF;YAC3BC,WAAW,IAAI,CAACN,KAAK,CAACM,SAAS;YAC/BY;QACF;IACF;IAEA,MAAcgE,kBAAkBhE,SAAiB,EAAEoB,WAAmB,EAAmB;QACvF,MAAMmD,oBAAoB;QAE1B,IAAI;YACF,MAAMC,WAAW,MAAM1G,YAAY;gBACjCsD;gBACAqD,OAAOF;gBACPvE;YACF;YAEA,IAAI,CAACwE,UAAUE,SAAS9E,QAAQ;gBAC9B,IAAI,CAACC,KAAK,CAAC,oBAAoB;oBAACC,MAAMnD,UAAU+D,aAAa;gBAAA;YAC/D;YAEA,MAAMiE,kBAAkBH,SAASE,OAAO,CAACE,GAAG,CAAC,CAACC,SAAwB,CAAA;oBACpE9B,MAAM8B,OAAOC,EAAE;oBACfC,OAAOF,OAAOC,EAAE;gBAClB,CAAA;YAEA,MAAME,OACJL,gBAAgB/E,MAAM,KAAK2E,oBACvB,CAAC,YAAY,EAAEA,kBAAkB,OAAO,CAAC,GACzC;YAEN,OAAOvH,OAAO;gBACZiI,SAASN;gBACTpE,SAAS,CAAC,uBAAuB,EAAEyE,MAAM;YAC3C;QACF,EAAE,OAAO3B,KAAK;YACZ,MAAM9C,UAAU8C,eAAe7C,QAAQ6C,IAAI9C,OAAO,GAAGE,OAAO4C;YAC5DhG,oBAAoB,CAAC,oCAAoC,EAAE+D,YAAY,EAAE,EAAEb,SAAS,EAAE8C;YACtF,IAAI,CAACxD,KAAK,CAAC,CAAC,oCAAoC,EAAEuB,YAAY,EAAE,EAAEb,SAAS,EAAE;gBAC3ET,MAAMnD,UAAU+D,aAAa;YAC/B;QACF;IACF;AACF"}
@@ -60,13 +60,13 @@ export class EnableBackupCommand extends SanityCommand {
60
60
  const message = error instanceof Error ? error.message : String(error);
61
61
  enableBackupDebug(`Failed to list datasets: ${message}`, error);
62
62
  this.error(`Failed to list datasets: ${message}`, {
63
- exit: 1
63
+ exit: exitCodes.RUNTIME_ERROR
64
64
  });
65
65
  }
66
66
  const hasProduction = datasets.some((dataset)=>dataset.name === 'production');
67
67
  if (datasets.length === 0) {
68
68
  this.error('No datasets found in this project.', {
69
- exit: 1
69
+ exit: exitCodes.RUNTIME_ERROR
70
70
  });
71
71
  }
72
72
  if (dataset) {
@@ -94,7 +94,7 @@ export class EnableBackupCommand extends SanityCommand {
94
94
  const message = error instanceof Error ? error.message : String(error);
95
95
  enableBackupDebug(`Failed to create dataset ${newDatasetName}: ${message}`, error);
96
96
  this.error(`Failed to create dataset ${newDatasetName}: ${message}`, {
97
- exit: 1
97
+ exit: exitCodes.RUNTIME_ERROR
98
98
  });
99
99
  }
100
100
  }
@@ -112,7 +112,7 @@ export class EnableBackupCommand extends SanityCommand {
112
112
  const message = error instanceof Error ? error.message : String(error);
113
113
  enableBackupDebug(`Failed to enable backup for dataset`, error);
114
114
  this.error(`Enabling dataset backup failed: ${message}`, {
115
- exit: 1
115
+ exit: exitCodes.RUNTIME_ERROR
116
116
  });
117
117
  }
118
118
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/backups/enable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {NEW_DATASET_VALUE, promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {setBackup} from '../../services/backup.js'\nimport {createDataset, listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst enableBackupDebug = subdebug('backup:enable')\n\nexport class EnableBackupCommand extends SanityCommand<typeof EnableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to enable backup for',\n required: false,\n }),\n }\n\n static override description = 'Enable backup for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively enable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Enable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to enable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:enable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(EnableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n const hasProduction = datasets.some((dataset) => dataset.name === 'production')\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await promptForDataset({allowCreation: true, datasets})\n\n if (dataset === NEW_DATASET_VALUE) {\n const newDatasetName = await promptForDatasetName({\n default: hasProduction ? undefined : 'production',\n })\n\n try {\n await createDataset({\n datasetName: newDatasetName,\n projectId,\n })\n dataset = newDatasetName\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to create dataset ${newDatasetName}: ${message}`, error)\n this.error(`Failed to create dataset ${newDatasetName}: ${message}`, {exit: 1})\n }\n }\n }\n\n try {\n await setBackup({dataset, projectId, status: true})\n\n this.log(\n `${styleText(\n 'green',\n `Enabled backups for dataset ${dataset}.\\nPlease note that it may take up to 24 hours before the first backup is created.\\n`,\n )}`,\n )\n\n this.log(\n `${styleText('bold', `Retention policies may apply depending on your plan and agreement.\\n`)}`,\n )\n\n enableBackupDebug(`Successfully enabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to enable backup for dataset`, error)\n this.error(`Enabling dataset backup failed: ${message}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Args","exitCodes","SanityCommand","subdebug","assertDatasetExists","NEW_DATASET_VALUE","promptForDataset","promptForDatasetName","promptForProject","setBackup","createDataset","listDatasets","getProjectIdFlag","enableBackupDebug","EnableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","hasProduction","some","name","length","output","isUnattended","USAGE_ERROR","allowCreation","newDatasetName","default","undefined","datasetName","status","log"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/backups/enable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {NEW_DATASET_VALUE, promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {setBackup} from '../../services/backup.js'\nimport {createDataset, listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst enableBackupDebug = subdebug('backup:enable')\n\nexport class EnableBackupCommand extends SanityCommand<typeof EnableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to enable backup for',\n required: false,\n }),\n }\n\n static override description = 'Enable backup for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively enable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Enable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to enable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:enable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(EnableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const hasProduction = datasets.some((dataset) => dataset.name === 'production')\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await promptForDataset({allowCreation: true, datasets})\n\n if (dataset === NEW_DATASET_VALUE) {\n const newDatasetName = await promptForDatasetName({\n default: hasProduction ? undefined : 'production',\n })\n\n try {\n await createDataset({\n datasetName: newDatasetName,\n projectId,\n })\n dataset = newDatasetName\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to create dataset ${newDatasetName}: ${message}`, error)\n this.error(`Failed to create dataset ${newDatasetName}: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n }\n\n try {\n await setBackup({dataset, projectId, status: true})\n\n this.log(\n `${styleText(\n 'green',\n `Enabled backups for dataset ${dataset}.\\nPlease note that it may take up to 24 hours before the first backup is created.\\n`,\n )}`,\n )\n\n this.log(\n `${styleText('bold', `Retention policies may apply depending on your plan and agreement.\\n`)}`,\n )\n\n enableBackupDebug(`Successfully enabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to enable backup for dataset`, error)\n this.error(`Enabling dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n"],"names":["styleText","Args","exitCodes","SanityCommand","subdebug","assertDatasetExists","NEW_DATASET_VALUE","promptForDataset","promptForDatasetName","promptForProject","setBackup","createDataset","listDatasets","getProjectIdFlag","enableBackupDebug","EnableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","hasProduction","some","name","length","output","isUnattended","USAGE_ERROR","allowCreation","newDatasetName","default","undefined","datasetName","status","log"],"mappings":"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"}
@@ -79,12 +79,12 @@ export class ListBackupCommand extends SanityCommand {
79
79
  const message = error instanceof Error ? error.message : String(error);
80
80
  listBackupDebug(`Failed to list datasets: ${message}`, error);
81
81
  this.error(`Failed to list datasets: ${message}`, {
82
- exit: 1
82
+ exit: exitCodes.RUNTIME_ERROR
83
83
  });
84
84
  }
85
85
  if (datasets.length === 0) {
86
86
  this.error('No datasets found in this project.', {
87
- exit: 1
87
+ exit: exitCodes.RUNTIME_ERROR
88
88
  });
89
89
  }
90
90
  if (dataset) {
@@ -103,19 +103,19 @@ export class ListBackupCommand extends SanityCommand {
103
103
  const parsedAfter = this.processDateFlag(flags.after, 'after');
104
104
  if (parsedAfter && parsedBefore && isAfter(parsedAfter, parsedBefore)) {
105
105
  this.error('--after date must be before --before', {
106
- exit: 1
106
+ exit: exitCodes.RUNTIME_ERROR
107
107
  });
108
108
  }
109
109
  } catch (err) {
110
110
  this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {
111
- exit: 1
111
+ exit: exitCodes.RUNTIME_ERROR
112
112
  });
113
113
  }
114
114
  }
115
115
  // Validate limit flag
116
116
  if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {
117
117
  this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {
118
- exit: 1
118
+ exit: exitCodes.RUNTIME_ERROR
119
119
  });
120
120
  }
121
121
  const query = {
@@ -172,7 +172,7 @@ export class ListBackupCommand extends SanityCommand {
172
172
  const message = error instanceof Error ? error.message : String(error);
173
173
  listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error);
174
174
  this.error(`List dataset backup failed: ${message}`, {
175
- exit: 1
175
+ exit: exitCodes.RUNTIME_ERROR
176
176
  });
177
177
  }
178
178
  }
@@ -197,7 +197,7 @@ export class ListBackupCommand extends SanityCommand {
197
197
  } catch (error) {
198
198
  listBackupDebug(`Error selecting dataset`, error);
199
199
  this.error(`Failed to select dataset:\n${error instanceof Error ? error.message : error}`, {
200
- exit: 1
200
+ exit: exitCodes.RUNTIME_ERROR
201
201
  });
202
202
  }
203
203
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport {Table} from 'console-table-printer'\nimport {isAfter} from 'date-fns/isAfter'\nimport {isValid} from 'date-fns/isValid'\nimport {lightFormat} from 'date-fns/lightFormat'\nimport {parse} from 'date-fns/parse'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:list']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && isAfter(parsedAfter, parsedBefore)) {\n this.error('--after date must be before --before', {exit: 1})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {exit: 1})\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: 1,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: lightFormat(Date.parse(createdAt), 'yyyy-MM-dd HH:mm:ss'),\n resource: 'Dataset',\n })\n }\n\n table.printTable()\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: 1})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parse(date, 'yyyy-MM-dd', new Date())\n if (isValid(parsedDate)) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: 1,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","select","Table","isAfter","isValid","lightFormat","parse","assertDatasetExists","promptForProject","listBackups","listDatasets","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","Date","resource","printTable","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport {Table} from 'console-table-printer'\nimport {isAfter} from 'date-fns/isAfter'\nimport {isValid} from 'date-fns/isValid'\nimport {lightFormat} from 'date-fns/lightFormat'\nimport {parse} from 'date-fns/parse'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:list']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && isAfter(parsedAfter, parsedBefore)) {\n this.error('--after date must be before --before', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: lightFormat(Date.parse(createdAt), 'yyyy-MM-dd HH:mm:ss'),\n resource: 'Dataset',\n })\n }\n\n table.printTable()\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parse(date, 'yyyy-MM-dd', new Date())\n if (isValid(parsedDate)) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","select","Table","isAfter","isValid","lightFormat","parse","assertDatasetExists","promptForProject","listBackups","listDatasets","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","Date","resource","printTable","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { execSync, spawn } from 'node:child_process';
2
2
  import { existsSync } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { Args, Flags } from '@oclif/core';
5
- import { SanityCommand, subdebug } from '@sanity/cli-core';
5
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
6
6
  import { codemods } from '../actions/codemods/index.js';
7
7
  const codemodeDebug = subdebug('codemod');
8
8
  export class CodemodCommand extends SanityCommand {
@@ -55,7 +55,7 @@ export class CodemodCommand extends SanityCommand {
55
55
  const mod = normalizedMods.get(codemodName.toLowerCase());
56
56
  if (!mod) {
57
57
  this.error(`Codemod with name "${codemodName}" not found`, {
58
- exit: 1
58
+ exit: exitCodes.RUNTIME_ERROR
59
59
  });
60
60
  }
61
61
  // Verify if there is any verification defined, and user has not opted out of it
@@ -68,7 +68,7 @@ export class CodemodCommand extends SanityCommand {
68
68
  const err = error instanceof Error ? error : new Error(String(error));
69
69
  codemodeDebug('Verification failed: %s', err.message);
70
70
  this.error(`Verification failed: ${err.message}`, {
71
- exit: 1
71
+ exit: exitCodes.RUNTIME_ERROR
72
72
  });
73
73
  }
74
74
  }
@@ -121,12 +121,12 @@ export class CodemodCommand extends SanityCommand {
121
121
  });
122
122
  if (!npxHelp.includes('npm')) {
123
123
  this.error('Not the npx we expected', {
124
- exit: 1
124
+ exit: exitCodes.RUNTIME_ERROR
125
125
  });
126
126
  }
127
127
  } catch {
128
128
  this.error(`Failed to run "npx" - required to run codemods. Do you have a recent version of npm installed?`, {
129
- exit: 1
129
+ exit: exitCodes.RUNTIME_ERROR
130
130
  });
131
131
  }
132
132
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/codemod.ts"],"sourcesContent":["import {execSync, spawn} from 'node:child_process'\nimport {existsSync} from 'node:fs'\nimport path from 'node:path'\n\nimport {Args, Flags} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {codemods} from '../actions/codemods/index.js'\nimport {type CodeMod} from '../actions/codemods/types.js'\n\nconst codemodeDebug = subdebug('codemod')\n\nexport class CodemodCommand extends SanityCommand<typeof CodemodCommand> {\n static override args = {\n codemodName: Args.string({\n description: 'Name of the codemod to run',\n required: false,\n }),\n }\n\n static override description = 'Updates Sanity Studio codebase with a code modification script'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Show available code mods',\n },\n {\n command: '<%= config.bin %> <%= command.id %> reactIconsV3 --dry',\n description: 'Run codemod to transform react-icons imports (dry run)',\n },\n ]\n\n static override flags = {\n dry: Flags.boolean({\n default: false,\n description: 'Dry run (no changes are made to files)',\n }),\n extensions: Flags.string({\n default: 'js,ts,tsx',\n description: 'Transform files with these file extensions (comma separated)',\n }),\n 'no-verify': Flags.boolean({\n default: false,\n description: 'Skip verification steps before running codemod',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CodemodCommand)\n const {codemodName} = args\n const workDir = process.cwd()\n\n if (!codemodName) {\n this.printMods()\n return\n }\n\n // Make a record where all the keys of the available codemods are lowercased,\n // eg `reactIconsV3` turns into `{reacticonsv3: CodeMod}`\n const normalizedMods: Map<string, CodeMod> = new Map()\n for (const [originalName, mod] of Object.entries(codemods)) {\n normalizedMods.set(originalName.toLowerCase(), mod)\n }\n\n // Verify that mod exists\n const mod = normalizedMods.get(codemodName.toLowerCase())\n if (!mod) {\n this.error(`Codemod with name \"${codemodName}\" not found`, {exit: 1})\n }\n\n // Verify if there is any verification defined, and user has not opted out of it\n if (typeof mod.verify === 'function' && !flags['no-verify']) {\n try {\n await mod.verify({workDir})\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n codemodeDebug('Verification failed: %s', err.message)\n this.error(`Verification failed: ${err.message}`, {exit: 1})\n }\n }\n\n // If extensions are defined, they come in as CSV, so split and normalize them\n const exts = flags.extensions\n ? flags.extensions.split(',').map((ext) => ext.trim().replace(/^\\./, ''))\n : ['js', 'ts', 'tsx']\n\n const dryRun = flags.dry\n\n // We use npx to run jscodeshift, so verify that it exists and works before using it\n this.ensureNpx()\n\n // If there is a gitignore in the target folder, we want to use that to know\n // which folders to ignore - eg `dist`, `coverage` or whatever\n const hasGitIgnore = existsSync(path.join(workDir, '.gitignore'))\n\n // Build the CLI command arguments\n const cliRoot = path.resolve(import.meta.dirname, '../..')\n const modPath = path.resolve(path.join(cliRoot, 'codemods', mod.filename))\n const cmdArgs = [\n 'jscodeshift',\n '--ignore-pattern',\n 'node_modules',\n '--ignore-pattern',\n 'dist',\n hasGitIgnore && '--ignore-config',\n hasGitIgnore && '.gitignore',\n '-t',\n modPath,\n '--extensions',\n exts.join(','),\n dryRun && '--dry',\n workDir,\n ].filter((item): item is string => typeof item === 'string')\n\n const child = spawn('npx', cmdArgs, {\n stdio: 'inherit',\n })\n\n const handleSigint = () => {\n child.kill(2)\n }\n\n process.on('SIGINT', handleSigint)\n\n child.on('close', (code) => {\n process.removeListener('SIGINT', handleSigint)\n this.exit(code || undefined)\n })\n }\n\n private ensureNpx(): void {\n try {\n const npxHelp = execSync('npx --help', {encoding: 'utf8'})\n if (!npxHelp.includes('npm')) {\n this.error('Not the npx we expected', {exit: 1})\n }\n } catch {\n this.error(\n `Failed to run \"npx\" - required to run codemods. Do you have a recent version of npm installed?`,\n {exit: 1},\n )\n }\n }\n\n private printMods(): void {\n this.log('Available code modifications:\\n')\n\n for (const [modName, mod] of Object.entries(codemods)) {\n this.log(`${modName} - ${mod.purpose}`)\n }\n }\n}\n"],"names":["execSync","spawn","existsSync","path","Args","Flags","SanityCommand","subdebug","codemods","codemodeDebug","CodemodCommand","args","codemodName","string","description","required","examples","command","flags","dry","boolean","default","extensions","run","parse","workDir","process","cwd","printMods","normalizedMods","Map","originalName","mod","Object","entries","set","toLowerCase","get","error","exit","verify","err","Error","String","message","exts","split","map","ext","trim","replace","dryRun","ensureNpx","hasGitIgnore","join","cliRoot","resolve","dirname","modPath","filename","cmdArgs","filter","item","child","stdio","handleSigint","kill","on","code","removeListener","undefined","npxHelp","encoding","includes","log","modName","purpose"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/commands/codemod.ts"],"sourcesContent":["import {execSync, spawn} from 'node:child_process'\nimport {existsSync} from 'node:fs'\nimport path from 'node:path'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {codemods} from '../actions/codemods/index.js'\nimport {type CodeMod} from '../actions/codemods/types.js'\n\nconst codemodeDebug = subdebug('codemod')\n\nexport class CodemodCommand extends SanityCommand<typeof CodemodCommand> {\n static override args = {\n codemodName: Args.string({\n description: 'Name of the codemod to run',\n required: false,\n }),\n }\n\n static override description = 'Updates Sanity Studio codebase with a code modification script'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Show available code mods',\n },\n {\n command: '<%= config.bin %> <%= command.id %> reactIconsV3 --dry',\n description: 'Run codemod to transform react-icons imports (dry run)',\n },\n ]\n\n static override flags = {\n dry: Flags.boolean({\n default: false,\n description: 'Dry run (no changes are made to files)',\n }),\n extensions: Flags.string({\n default: 'js,ts,tsx',\n description: 'Transform files with these file extensions (comma separated)',\n }),\n 'no-verify': Flags.boolean({\n default: false,\n description: 'Skip verification steps before running codemod',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CodemodCommand)\n const {codemodName} = args\n const workDir = process.cwd()\n\n if (!codemodName) {\n this.printMods()\n return\n }\n\n // Make a record where all the keys of the available codemods are lowercased,\n // eg `reactIconsV3` turns into `{reacticonsv3: CodeMod}`\n const normalizedMods: Map<string, CodeMod> = new Map()\n for (const [originalName, mod] of Object.entries(codemods)) {\n normalizedMods.set(originalName.toLowerCase(), mod)\n }\n\n // Verify that mod exists\n const mod = normalizedMods.get(codemodName.toLowerCase())\n if (!mod) {\n this.error(`Codemod with name \"${codemodName}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n // Verify if there is any verification defined, and user has not opted out of it\n if (typeof mod.verify === 'function' && !flags['no-verify']) {\n try {\n await mod.verify({workDir})\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n codemodeDebug('Verification failed: %s', err.message)\n this.error(`Verification failed: ${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n // If extensions are defined, they come in as CSV, so split and normalize them\n const exts = flags.extensions\n ? flags.extensions.split(',').map((ext) => ext.trim().replace(/^\\./, ''))\n : ['js', 'ts', 'tsx']\n\n const dryRun = flags.dry\n\n // We use npx to run jscodeshift, so verify that it exists and works before using it\n this.ensureNpx()\n\n // If there is a gitignore in the target folder, we want to use that to know\n // which folders to ignore - eg `dist`, `coverage` or whatever\n const hasGitIgnore = existsSync(path.join(workDir, '.gitignore'))\n\n // Build the CLI command arguments\n const cliRoot = path.resolve(import.meta.dirname, '../..')\n const modPath = path.resolve(path.join(cliRoot, 'codemods', mod.filename))\n const cmdArgs = [\n 'jscodeshift',\n '--ignore-pattern',\n 'node_modules',\n '--ignore-pattern',\n 'dist',\n hasGitIgnore && '--ignore-config',\n hasGitIgnore && '.gitignore',\n '-t',\n modPath,\n '--extensions',\n exts.join(','),\n dryRun && '--dry',\n workDir,\n ].filter((item): item is string => typeof item === 'string')\n\n const child = spawn('npx', cmdArgs, {\n stdio: 'inherit',\n })\n\n const handleSigint = () => {\n child.kill(2)\n }\n\n process.on('SIGINT', handleSigint)\n\n child.on('close', (code) => {\n process.removeListener('SIGINT', handleSigint)\n this.exit(code || undefined)\n })\n }\n\n private ensureNpx(): void {\n try {\n const npxHelp = execSync('npx --help', {encoding: 'utf8'})\n if (!npxHelp.includes('npm')) {\n this.error('Not the npx we expected', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch {\n this.error(\n `Failed to run \"npx\" - required to run codemods. Do you have a recent version of npm installed?`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n\n private printMods(): void {\n this.log('Available code modifications:\\n')\n\n for (const [modName, mod] of Object.entries(codemods)) {\n this.log(`${modName} - ${mod.purpose}`)\n }\n }\n}\n"],"names":["execSync","spawn","existsSync","path","Args","Flags","exitCodes","SanityCommand","subdebug","codemods","codemodeDebug","CodemodCommand","args","codemodName","string","description","required","examples","command","flags","dry","boolean","default","extensions","run","parse","workDir","process","cwd","printMods","normalizedMods","Map","originalName","mod","Object","entries","set","toLowerCase","get","error","exit","RUNTIME_ERROR","verify","err","Error","String","message","exts","split","map","ext","trim","replace","dryRun","ensureNpx","hasGitIgnore","join","cliRoot","resolve","dirname","modPath","filename","cmdArgs","filter","item","child","stdio","handleSigint","kill","on","code","removeListener","undefined","npxHelp","encoding","includes","log","modName","purpose"],"mappings":"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"}
@@ -105,7 +105,7 @@ export class Add extends SanityCommand {
105
105
  const err = error;
106
106
  addCorsDebug(`Error adding CORS origin`, err);
107
107
  this.error(`CORS origin addition failed:\n${err.message}`, {
108
- exit: 1
108
+ exit: exitCodes.RUNTIME_ERROR
109
109
  });
110
110
  }
111
111
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/cors/add.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {confirm, logSymbols} from '@sanity/cli-core/ux'\nimport {oneline} from 'oneline'\n\nimport {filterAndValidateOrigin} from '../../actions/cors/filterAndValidateOrigin.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {createCorsOrigin} from '../../services/cors.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst addCorsDebug = subdebug('cors:add')\n\nexport class Add extends SanityCommand<typeof Add> {\n static override args = {\n origin: Args.string({\n description: 'Origin to allow (e.g., https://example.com)',\n required: true,\n }),\n }\n\n static override description = 'Add a CORS origin to the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively add a CORS origin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> http://localhost:3000 --no-credentials',\n description: 'Add a localhost origin without credentials',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://myapp.com --credentials',\n description: 'Add a production origin with credentials allowed',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://myapp.com --project-id abc123',\n description: 'Add a CORS origin for a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to add CORS origin to',\n semantics: 'override',\n }),\n credentials: Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Allow credentials (token/cookie) to be sent from this origin',\n required: false,\n }),\n yes: Flags.boolean({\n char: 'y',\n description: 'Confirm risky wildcard origins without prompting',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(Add)\n const {origin} = args\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'create', permission: 'sanity.project.cors'}],\n }),\n })\n\n // Check if the origin argument looks like a file path and warn\n try {\n const isFile = fs.existsSync(path.join(process.cwd(), args.origin))\n if (isFile) {\n this.warn(`Origin \"${args.origin}?\" Remember to quote values (sanity cors add \"*\")`)\n }\n } catch {\n // Ignore errors checking if it's a file\n }\n\n const filteredOrigin = await filterAndValidateOrigin(origin, this.output)\n const hasWildcard = origin.includes('*')\n\n if (hasWildcard) {\n if (this.isUnattended() && !flags.yes) {\n this.error('Wildcard origins require confirmation. Pass `--yes` to continue.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const confirmed = flags.yes || (await this.promptForWildcardConfirmation(origin))\n if (!confirmed) {\n this.log('CORS origin not added')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n const allowCredentials =\n flags.credentials === undefined\n ? this.isUnattended()\n ? false\n : await this.promptForCredentials(hasWildcard)\n : Boolean(flags.credentials)\n\n if (filteredOrigin !== origin) {\n this.log(`Normalized origin to: ${filteredOrigin}`)\n }\n\n try {\n const response = await createCorsOrigin({\n allowCredentials,\n origin: filteredOrigin,\n projectId,\n })\n\n addCorsDebug(`CORS origin added successfully`, response)\n\n this.log('CORS origin added')\n } catch (error) {\n const err = error as Error\n\n addCorsDebug(`Error adding CORS origin`, err)\n this.error(`CORS origin addition failed:\\n${err.message}`, {exit: 1})\n }\n }\n\n /**\n * Prompt the user for credentials\n *\n * @param hasWildcard - Whether the origin contains a wildcard\n * @returns - Whether to allow credentials\n */\n private async promptForCredentials(hasWildcard: boolean) {\n this.log('')\n if (hasWildcard) {\n this.log(oneline`\n ${styleText('yellow', `${logSymbols.warning} Warning:`)}\n We ${styleText(['red', 'underline'], 'HIGHLY')} recommend NOT allowing credentials\n on origins containing wildcards. If you are logged in to a Sanity studio or an app built with\n the Sanity App SDK, people will be able to send requests ${styleText('underline', 'on your behalf')}\n to read and modify data, from any matching origin. Please tread carefully!\n `)\n } else {\n this.log(oneline`\n ${styleText('yellow', `${logSymbols.warning} Warning:`)}\n Should this origin be allowed to send requests using authentication tokens or\n session cookies? Be aware that any script on this origin will be able to send\n requests ${styleText('underline', 'on your behalf')} to read and modify data if you\n are logged in to a Sanity studio or app. If this origin hosts a studio or an app built with\n the Sanity App SDK, you will need this, otherwise you should probably answer \"No\" (n).\n `)\n }\n\n this.log('')\n\n return confirm({\n default: false,\n message: oneline`\n Allow credentials to be sent from this origin? Please read the warning above.\n `,\n })\n }\n\n /**\n * Prompt the user for wildcard confirmation\n *\n * @param origin - The origin to check for wildcards\n * @returns - Whether to allow the origin\n */\n private async promptForWildcardConfirmation(origin: string) {\n this.log('')\n this.log(styleText('yellow', `${logSymbols.warning} Warning: Examples of allowed origins:`))\n\n if (origin === '*') {\n this.log('- http://www.some-malicious.site')\n this.log('- https://not.what-you-were-expecting.com')\n this.log('- https://high-traffic-site.com')\n this.log('- http://192.168.1.1:8080')\n } else {\n this.log(`- ${origin.replace(/:\\*/, ':1234').replaceAll('*', 'foo')}`)\n this.log(`- ${origin.replace(/:\\*/, ':3030').replaceAll('*', 'foo.bar')}`)\n }\n\n this.log('')\n\n return confirm({\n default: false,\n message: oneline`\n Using wildcards can be ${styleText('red', 'risky')}.\n Are you ${styleText('underline', 'absolutely sure')} you want to allow this origin?`,\n })\n }\n}\n"],"names":["fs","path","styleText","Args","Flags","exitCodes","SanityCommand","subdebug","confirm","logSymbols","oneline","filterAndValidateOrigin","promptForProject","createCorsOrigin","getProjectIdFlag","addCorsDebug","Add","args","origin","string","description","required","examples","command","flags","semantics","credentials","boolean","allowNo","default","undefined","yes","char","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","isFile","existsSync","join","process","cwd","warn","filteredOrigin","output","hasWildcard","includes","isUnattended","error","exit","USAGE_ERROR","confirmed","promptForWildcardConfirmation","log","USER_ABORT","allowCredentials","promptForCredentials","Boolean","response","err","message","warning","replace","replaceAll"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/cors/add.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {confirm, logSymbols} from '@sanity/cli-core/ux'\nimport {oneline} from 'oneline'\n\nimport {filterAndValidateOrigin} from '../../actions/cors/filterAndValidateOrigin.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {createCorsOrigin} from '../../services/cors.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst addCorsDebug = subdebug('cors:add')\n\nexport class Add extends SanityCommand<typeof Add> {\n static override args = {\n origin: Args.string({\n description: 'Origin to allow (e.g., https://example.com)',\n required: true,\n }),\n }\n\n static override description = 'Add a CORS origin to the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively add a CORS origin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> http://localhost:3000 --no-credentials',\n description: 'Add a localhost origin without credentials',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://myapp.com --credentials',\n description: 'Add a production origin with credentials allowed',\n },\n {\n command: '<%= config.bin %> <%= command.id %> https://myapp.com --project-id abc123',\n description: 'Add a CORS origin for a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to add CORS origin to',\n semantics: 'override',\n }),\n credentials: Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Allow credentials (token/cookie) to be sent from this origin',\n required: false,\n }),\n yes: Flags.boolean({\n char: 'y',\n description: 'Confirm risky wildcard origins without prompting',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(Add)\n const {origin} = args\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'create', permission: 'sanity.project.cors'}],\n }),\n })\n\n // Check if the origin argument looks like a file path and warn\n try {\n const isFile = fs.existsSync(path.join(process.cwd(), args.origin))\n if (isFile) {\n this.warn(`Origin \"${args.origin}?\" Remember to quote values (sanity cors add \"*\")`)\n }\n } catch {\n // Ignore errors checking if it's a file\n }\n\n const filteredOrigin = await filterAndValidateOrigin(origin, this.output)\n const hasWildcard = origin.includes('*')\n\n if (hasWildcard) {\n if (this.isUnattended() && !flags.yes) {\n this.error('Wildcard origins require confirmation. Pass `--yes` to continue.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const confirmed = flags.yes || (await this.promptForWildcardConfirmation(origin))\n if (!confirmed) {\n this.log('CORS origin not added')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n const allowCredentials =\n flags.credentials === undefined\n ? this.isUnattended()\n ? false\n : await this.promptForCredentials(hasWildcard)\n : Boolean(flags.credentials)\n\n if (filteredOrigin !== origin) {\n this.log(`Normalized origin to: ${filteredOrigin}`)\n }\n\n try {\n const response = await createCorsOrigin({\n allowCredentials,\n origin: filteredOrigin,\n projectId,\n })\n\n addCorsDebug(`CORS origin added successfully`, response)\n\n this.log('CORS origin added')\n } catch (error) {\n const err = error as Error\n\n addCorsDebug(`Error adding CORS origin`, err)\n this.error(`CORS origin addition failed:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n /**\n * Prompt the user for credentials\n *\n * @param hasWildcard - Whether the origin contains a wildcard\n * @returns - Whether to allow credentials\n */\n private async promptForCredentials(hasWildcard: boolean) {\n this.log('')\n if (hasWildcard) {\n this.log(oneline`\n ${styleText('yellow', `${logSymbols.warning} Warning:`)}\n We ${styleText(['red', 'underline'], 'HIGHLY')} recommend NOT allowing credentials\n on origins containing wildcards. If you are logged in to a Sanity studio or an app built with\n the Sanity App SDK, people will be able to send requests ${styleText('underline', 'on your behalf')}\n to read and modify data, from any matching origin. Please tread carefully!\n `)\n } else {\n this.log(oneline`\n ${styleText('yellow', `${logSymbols.warning} Warning:`)}\n Should this origin be allowed to send requests using authentication tokens or\n session cookies? Be aware that any script on this origin will be able to send\n requests ${styleText('underline', 'on your behalf')} to read and modify data if you\n are logged in to a Sanity studio or app. If this origin hosts a studio or an app built with\n the Sanity App SDK, you will need this, otherwise you should probably answer \"No\" (n).\n `)\n }\n\n this.log('')\n\n return confirm({\n default: false,\n message: oneline`\n Allow credentials to be sent from this origin? Please read the warning above.\n `,\n })\n }\n\n /**\n * Prompt the user for wildcard confirmation\n *\n * @param origin - The origin to check for wildcards\n * @returns - Whether to allow the origin\n */\n private async promptForWildcardConfirmation(origin: string) {\n this.log('')\n this.log(styleText('yellow', `${logSymbols.warning} Warning: Examples of allowed origins:`))\n\n if (origin === '*') {\n this.log('- http://www.some-malicious.site')\n this.log('- https://not.what-you-were-expecting.com')\n this.log('- https://high-traffic-site.com')\n this.log('- http://192.168.1.1:8080')\n } else {\n this.log(`- ${origin.replace(/:\\*/, ':1234').replaceAll('*', 'foo')}`)\n this.log(`- ${origin.replace(/:\\*/, ':3030').replaceAll('*', 'foo.bar')}`)\n }\n\n this.log('')\n\n return confirm({\n default: false,\n message: oneline`\n Using wildcards can be ${styleText('red', 'risky')}.\n Are you ${styleText('underline', 'absolutely sure')} you want to allow this origin?`,\n })\n }\n}\n"],"names":["fs","path","styleText","Args","Flags","exitCodes","SanityCommand","subdebug","confirm","logSymbols","oneline","filterAndValidateOrigin","promptForProject","createCorsOrigin","getProjectIdFlag","addCorsDebug","Add","args","origin","string","description","required","examples","command","flags","semantics","credentials","boolean","allowNo","default","undefined","yes","char","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","isFile","existsSync","join","process","cwd","warn","filteredOrigin","output","hasWildcard","includes","isUnattended","error","exit","USAGE_ERROR","confirmed","promptForWildcardConfirmation","log","USER_ABORT","allowCredentials","promptForCredentials","Boolean","response","err","message","RUNTIME_ERROR","warning","replace","replaceAll"],"mappings":"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"}
@@ -63,7 +63,7 @@ export class Delete extends SanityCommand {
63
63
  const err = error;
64
64
  deleteCorsDebug(`Error deleting CORS origin ${originId} for project ${projectId}`, err);
65
65
  this.error(`Origin deletion failed:\n${err.message}`, {
66
- exit: 1
66
+ exit: exitCodes.RUNTIME_ERROR
67
67
  });
68
68
  }
69
69
  }
@@ -75,12 +75,12 @@ export class Delete extends SanityCommand {
75
75
  const err = error;
76
76
  deleteCorsDebug(`Error fetching CORS origins for project ${projectId}`, err);
77
77
  this.error(`Failed to fetch CORS origins:\n${err.message}`, {
78
- exit: 1
78
+ exit: exitCodes.RUNTIME_ERROR
79
79
  });
80
80
  }
81
81
  if (origins.length === 0) {
82
82
  this.error('No CORS origins configured for this project.', {
83
- exit: 1
83
+ exit: exitCodes.RUNTIME_ERROR
84
84
  });
85
85
  }
86
86
  // If origin is specified, find it in the list
@@ -89,7 +89,7 @@ export class Delete extends SanityCommand {
89
89
  const selectedOrigin = origins.find((origin)=>origin.origin.toLowerCase() === specifiedOriginLower);
90
90
  if (!selectedOrigin) {
91
91
  this.error(`Origin "${specifiedOrigin}" not found`, {
92
- exit: 1
92
+ exit: exitCodes.RUNTIME_ERROR
93
93
  });
94
94
  }
95
95
  return selectedOrigin.id;