@sanity/cli 8.0.1 → 8.0.3

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 (119) hide show
  1. package/dist/SanityHelp.js.map +1 -1
  2. package/dist/actions/backup/assertDatasetExist.js +2 -1
  3. package/dist/actions/backup/assertDatasetExist.js.map +1 -1
  4. package/dist/actions/backup/downloadAsset.js +3 -24
  5. package/dist/actions/backup/downloadAsset.js.map +1 -1
  6. package/dist/actions/backup/downloadDocument.js +11 -22
  7. package/dist/actions/backup/downloadDocument.js.map +1 -1
  8. package/dist/actions/backup/downloadStream.js +83 -0
  9. package/dist/actions/backup/downloadStream.js.map +1 -0
  10. package/dist/actions/build/buildStudio.js +3 -2
  11. package/dist/actions/build/buildStudio.js.map +1 -1
  12. package/dist/actions/build/eventListenerFactory.js +7 -6
  13. package/dist/actions/build/eventListenerFactory.js.map +1 -1
  14. package/dist/actions/build/shouldAutoUpdate.js +2 -1
  15. package/dist/actions/build/shouldAutoUpdate.js.map +1 -1
  16. package/dist/actions/cors/filterAndValidateOrigin.js +3 -2
  17. package/dist/actions/cors/filterAndValidateOrigin.js.map +1 -1
  18. package/dist/actions/deploy/createUserApplication.js +2 -1
  19. package/dist/actions/deploy/createUserApplication.js.map +1 -1
  20. package/dist/actions/deploy/deployRunner.js +3 -2
  21. package/dist/actions/deploy/deployRunner.js.map +1 -1
  22. package/dist/actions/deploy/deployStudio.js +3 -3
  23. package/dist/actions/deploy/deployStudio.js.map +1 -1
  24. package/dist/actions/deploy/findUserApplication.js +8 -7
  25. package/dist/actions/deploy/findUserApplication.js.map +1 -1
  26. package/dist/actions/dev/servers/startAppDevServer.js +2 -1
  27. package/dist/actions/dev/servers/startAppDevServer.js.map +1 -1
  28. package/dist/actions/dev/servers/startStudioDevServer.js +3 -3
  29. package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
  30. package/dist/actions/documents/validate.js +5 -5
  31. package/dist/actions/documents/validate.js.map +1 -1
  32. package/dist/actions/documents/validateDocuments.worker.js +4 -1
  33. package/dist/actions/documents/validateDocuments.worker.js.map +1 -1
  34. package/dist/actions/init/env/createOrAppendEnvVars.js +2 -1
  35. package/dist/actions/init/env/createOrAppendEnvVars.js.map +1 -1
  36. package/dist/actions/init/env/parseAndUpdateEnvVars.js +2 -2
  37. package/dist/actions/init/env/parseAndUpdateEnvVars.js.map +1 -1
  38. package/dist/actions/schema/extractSchema.js +2 -2
  39. package/dist/actions/schema/extractSchema.js.map +1 -1
  40. package/dist/actions/schema/extractSchemaWatcher.js +2 -1
  41. package/dist/actions/schema/extractSchemaWatcher.js.map +1 -1
  42. package/dist/actions/schema/uploadSchemaToLexicon.js +3 -0
  43. package/dist/actions/schema/uploadSchemaToLexicon.js.map +1 -1
  44. package/dist/actions/telemetry/telemetryDebug.js +2 -2
  45. package/dist/actions/telemetry/telemetryDebug.js.map +1 -1
  46. package/dist/actions/undeploy/runUndeploy.js +2 -2
  47. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  48. package/dist/commands/backups/list.js +5 -8
  49. package/dist/commands/backups/list.js.map +1 -1
  50. package/dist/commands/datasets/copy.js +5 -7
  51. package/dist/commands/datasets/copy.js.map +1 -1
  52. package/dist/commands/datasets/import.js +7 -10
  53. package/dist/commands/datasets/import.js.map +1 -1
  54. package/dist/commands/docs/search.js +2 -2
  55. package/dist/commands/docs/search.js.map +1 -1
  56. package/dist/commands/mcp/configure.js +2 -2
  57. package/dist/commands/mcp/configure.js.map +1 -1
  58. package/dist/commands/typegen/generate.js +6 -6
  59. package/dist/commands/typegen/generate.js.map +1 -1
  60. package/dist/exports/_internal.d.ts +50 -82
  61. package/dist/exports/index.d.ts +48 -78
  62. package/dist/exports/invokeSanityCli/index.d.ts +108 -18
  63. package/dist/exports/invokeSanityCli/index.js +2 -1
  64. package/dist/exports/invokeSanityCli/index.js.map +1 -1
  65. package/dist/exports/runtime.d.ts +3 -65
  66. package/dist/services/api.js +23 -15
  67. package/dist/services/api.js.map +1 -1
  68. package/dist/services/auth.js +4 -4
  69. package/dist/services/auth.js.map +1 -1
  70. package/dist/services/backup.js +3 -3
  71. package/dist/services/backup.js.map +1 -1
  72. package/dist/services/cors.js +3 -3
  73. package/dist/services/cors.js.map +1 -1
  74. package/dist/services/datasetAliases.js +5 -5
  75. package/dist/services/datasetAliases.js.map +1 -1
  76. package/dist/services/datasets.js +3 -3
  77. package/dist/services/datasets.js.map +1 -1
  78. package/dist/services/documents.js +1 -1
  79. package/dist/services/documents.js.map +1 -1
  80. package/dist/services/getProjectFeatures.js +1 -1
  81. package/dist/services/getProjectFeatures.js.map +1 -1
  82. package/dist/services/getUrlHeaders.js +3 -9
  83. package/dist/services/getUrlHeaders.js.map +1 -1
  84. package/dist/services/grants.js +1 -1
  85. package/dist/services/grants.js.map +1 -1
  86. package/dist/services/graphql.js +4 -3
  87. package/dist/services/graphql.js.map +1 -1
  88. package/dist/services/hooks.js +6 -6
  89. package/dist/services/hooks.js.map +1 -1
  90. package/dist/services/mcp.js +4 -4
  91. package/dist/services/mcp.js.map +1 -1
  92. package/dist/services/mediaLibraries.js +3 -3
  93. package/dist/services/mediaLibraries.js.map +1 -1
  94. package/dist/services/mintProject.js +33 -14
  95. package/dist/services/mintProject.js.map +1 -1
  96. package/dist/services/organizations.js +6 -6
  97. package/dist/services/organizations.js.map +1 -1
  98. package/dist/services/plans.js +2 -2
  99. package/dist/services/plans.js.map +1 -1
  100. package/dist/services/projects.js +7 -7
  101. package/dist/services/projects.js.map +1 -1
  102. package/dist/services/schemas.js +2 -2
  103. package/dist/services/schemas.js.map +1 -1
  104. package/dist/services/telemetry.js +2 -2
  105. package/dist/services/telemetry.js.map +1 -1
  106. package/dist/services/tokens.js +5 -5
  107. package/dist/services/tokens.js.map +1 -1
  108. package/dist/services/user.js +1 -1
  109. package/dist/services/user.js.map +1 -1
  110. package/dist/services/userApplications.js +18 -18
  111. package/dist/services/userApplications.js.map +1 -1
  112. package/dist/util/appId.js +2 -1
  113. package/dist/util/appId.js.map +1 -1
  114. package/dist/util/dates.js +125 -0
  115. package/dist/util/dates.js.map +1 -0
  116. package/dist/util/getCliVersion.js +1 -11
  117. package/dist/util/getCliVersion.js.map +1 -1
  118. package/oclif.manifest.json +1093 -1093
  119. package/package.json +12 -15
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/documents/validateDocuments.worker.ts"],"sourcesContent":["import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport readline from 'node:readline'\nimport {Readable} from 'node:stream'\nimport {workerData as _workerData, isMainThread, parentPort} from 'node:worker_threads'\n\nimport {getStudioWorkspaces, getWorkspace, resolveLocalPackage} from '@sanity/cli-core'\nimport {type ClientConfig, createClient, type SanityDocument} from '@sanity/client'\nimport {type ValidationContext, type ValidationMarker} from '@sanity/types'\nimport {WorkerChannelReporter} from '@sanity/worker-channels'\nimport pMap from 'p-map'\n\nimport {\n type AvailabilityResponse,\n checkDocumentAvailability,\n exportDocuments,\n getDocumentCount,\n} from '../../services/documents.js'\nimport {extractDocumentsFromNdjsonOrTarball} from '../../util/extractDocumentsFromNdjsonOrTarball.js'\nimport {\n DOCUMENT_VALIDATION_TIMEOUT,\n getReferenceIds,\n isValidId,\n levelValues,\n MAX_VALIDATION_CONCURRENCY,\n REFERENCE_INTEGRITY_BATCH_SIZE,\n shouldIncludeDocument,\n} from '../../util/validation/validateDocumentsUtils.js'\nimport {type ValidateDocumentsWorkerData, type ValidationWorkerChannel} from './types.js'\n\nconst {\n clientConfig,\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n studioHost,\n workDir,\n workspace: workspaceName,\n} = _workerData as ValidateDocumentsWorkerData\n\nif (isMainThread || !parentPort) {\n throw new Error('This module must be run as a worker thread')\n}\n\nconst report = WorkerChannelReporter.from<ValidationWorkerChannel>(parentPort)\n\n// eslint-disable-next-line n/no-unsupported-features/node-builtins\nasync function* readerToGenerator(reader: ReadableStreamDefaultReader<Uint8Array>) {\n while (true) {\n const {done, value} = await reader.read()\n if (value) yield value\n if (done) return\n }\n}\n\nawait main()\nprocess.exit()\n\nasync function loadWorkspace() {\n const workspaces = await getStudioWorkspaces(workDir)\n const workspace = getWorkspace(workspaces, workspaceName)\n\n const resolvedDataset = dataset || workspace.dataset\n const resolvedProjectId = projectId || workspace.projectId\n\n const client = createClient({\n ...clientConfig,\n dataset: resolvedDataset,\n projectId: resolvedProjectId,\n requestTagPrefix: 'sanity.cli.validate',\n }).config({apiVersion: 'v2021-03-25'})\n\n report.event.loadedWorkspace({\n basePath: workspace.basePath,\n dataset: workspace.dataset,\n name: workspace.name,\n projectId: workspace.projectId,\n })\n\n return {client, dataset: resolvedDataset, projectId: resolvedProjectId, workspace}\n}\n\ninterface DownloadFromExportOptions {\n dataset: string\n projectId: string\n}\n\nasync function downloadFromExport({\n dataset: datasetName,\n projectId: projId,\n}: DownloadFromExportOptions) {\n const documentCount = await getDocumentCount({dataset: datasetName, projectId: projId})\n report.event.loadedDocumentCount({documentCount})\n\n const response = await exportDocuments({dataset: datasetName, projectId: projId})\n\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Could not get reader from response body.')\n\n let downloadedCount = 0\n const referencedIds = new Set<string>()\n const documentIds = new Set<string>()\n const lines = readline.createInterface({input: Readable.from(readerToGenerator(reader))})\n\n // Note: we stream the export to a file and then re-read from that file to\n // make this less memory intensive.\n // this is a similar pattern to the import/export CLI commands\n const slugDate = new Date()\n .toISOString()\n .replaceAll(/[^a-z0-9]/gi, '-')\n .toLowerCase()\n const tempOutputFile = path.join(os.tmpdir(), `sanity-validate-${slugDate}.ndjson`)\n const outputStream = fs.createWriteStream(tempOutputFile)\n\n for await (const line of lines) {\n const document = JSON.parse(line) as SanityDocument\n\n if (shouldIncludeDocument(document)) {\n documentIds.add(document._id)\n for (const referenceId of getReferenceIds(document)) {\n referencedIds.add(referenceId)\n }\n\n outputStream.write(`${line}\\n`)\n }\n\n downloadedCount++\n report.stream.exportProgress.emit({documentCount, downloadedCount})\n }\n\n await new Promise<void>((resolve, reject) =>\n outputStream.close((err) => (err ? reject(err) : resolve())),\n )\n\n report.stream.exportProgress.end()\n report.event.exportFinished({totalDocumentsToValidate: documentIds.size})\n\n const getDocuments = () =>\n extractDocumentsFromNdjsonOrTarball(fs.createReadStream(tempOutputFile))\n\n return {cleanup: () => fs.promises.rm(tempOutputFile), documentIds, getDocuments, referencedIds}\n}\n\nasync function downloadFromFile(filePath: string) {\n const referencedIds = new Set<string>()\n const documentIds = new Set<string>()\n const getDocuments = () => extractDocumentsFromNdjsonOrTarball(fs.createReadStream(filePath))\n\n for await (const document of getDocuments()) {\n if (shouldIncludeDocument(document)) {\n documentIds.add(document._id)\n for (const referenceId of getReferenceIds(document)) {\n referencedIds.add(referenceId)\n }\n }\n }\n\n report.event.exportFinished({totalDocumentsToValidate: documentIds.size})\n\n return {cleanup: undefined, documentIds, getDocuments, referencedIds}\n}\n\ninterface CheckReferenceExistenceOptions {\n dataset: string\n documentIds: Set<string>\n projectId: string\n referencedIds: Set<string>\n}\n\nasync function checkReferenceExistence({\n dataset: datasetName,\n documentIds,\n projectId: projId,\n referencedIds: _referencedIds,\n}: CheckReferenceExistenceOptions) {\n const existingIds = new Set(documentIds)\n const idsToCheck = [..._referencedIds]\n .filter((id) => !existingIds.has(id) && isValidId(id))\n .toSorted()\n\n const batches: string[][] = []\n for (let i = 0; i < idsToCheck.length; i += REFERENCE_INTEGRITY_BATCH_SIZE) {\n batches.push(idsToCheck.slice(i, i + REFERENCE_INTEGRITY_BATCH_SIZE))\n }\n\n for (const batch of batches) {\n const {omitted} = await checkDocumentAvailability({\n dataset: datasetName,\n documentIds: batch,\n projectId: projId,\n })\n\n const omittedIds: Record<string, AvailabilityResponse['omitted'][number]['reason']> = {}\n for (const item of omitted) {\n omittedIds[item.id] = item.reason\n }\n\n for (const id of batch) {\n // unless the document ID is in the `omitted` object explictly due to\n // the reason `'existence'`, then it should exist\n if (omittedIds[id] !== 'existence') {\n existingIds.add(id)\n }\n }\n }\n report.event.loadedReferenceIntegrity()\n\n return {existingIds}\n}\n\nasync function main() {\n let cleanupDownloadedDocuments: (() => Promise<void>) | undefined\n\n try {\n const {client, dataset: datasetName, projectId: projId, workspace} = await loadWorkspace()\n const {cleanup, documentIds, getDocuments, referencedIds} = ndjsonFilePath\n ? await downloadFromFile(ndjsonFilePath)\n : await downloadFromExport({dataset: datasetName, projectId: projId})\n cleanupDownloadedDocuments = cleanup\n const {existingIds} = await checkReferenceExistence({\n dataset: datasetName,\n documentIds,\n projectId: projId,\n referencedIds,\n })\n\n const getClient = <TOptions extends Partial<ClientConfig>>(options: TOptions) =>\n client.withConfig(options)\n\n const getDocumentExists: ValidationContext['getDocumentExists'] = ({id}) =>\n Promise.resolve(existingIds.has(id))\n\n const getLevel = (markers: ValidationMarker[]) => {\n let foundWarning = false\n for (const marker of markers) {\n if (marker.level === 'error') return 'error'\n if (marker.level === 'warning') foundWarning = true\n }\n\n if (foundWarning) return 'warning'\n return 'info'\n }\n\n let validatedCount = 0\n\n const validate = async (document: SanityDocument) => {\n let markers: ValidationMarker[]\n\n try {\n const timeout = Symbol('timeout')\n\n const {validateDocument} = await resolveLocalPackage<typeof import('sanity')>(\n 'sanity',\n workDir,\n )\n\n const result = await Promise.race([\n validateDocument({\n document,\n environment: 'cli',\n getClient,\n getDocumentExists,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n workspace,\n }),\n new Promise<typeof timeout>((resolve) =>\n setTimeout(() => resolve(timeout), DOCUMENT_VALIDATION_TIMEOUT),\n ),\n ])\n\n if (result === timeout) {\n throw new Error(\n `Document '${document._id}' failed to validate within ${DOCUMENT_VALIDATION_TIMEOUT}ms.`,\n )\n }\n\n markers = result\n // filter out unwanted levels\n .filter((marker) => {\n const markerValue = levelValues[marker.level]\n const flagLevelValue =\n levelValues[level as keyof typeof levelValues] ?? levelValues.info\n return markerValue <= flagLevelValue\n })\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error'\n\n const message = `Exception occurred while validating value: ${errorMessage}`\n\n markers = [\n {\n level: 'error',\n message,\n path: [],\n },\n ]\n }\n\n validatedCount++\n\n const intentUrl =\n studioHost &&\n `${studioHost}${path.resolve(\n workspace.basePath,\n `/intent/edit/id=${encodeURIComponent(document._id)};type=${encodeURIComponent(\n document._type,\n )}`,\n )}`\n\n report.stream.validation.emit({\n documentId: document._id,\n documentType: document._type,\n ...(intentUrl && {intentUrl}),\n level: getLevel(markers),\n markers,\n revision: document._rev,\n validatedCount,\n })\n }\n\n await pMap(getDocuments(), validate, {concurrency: MAX_VALIDATION_CONCURRENCY})\n\n report.stream.validation.end()\n } finally {\n await cleanupDownloadedDocuments?.()\n }\n}\n"],"names":["fs","os","path","readline","Readable","workerData","_workerData","isMainThread","parentPort","getStudioWorkspaces","getWorkspace","resolveLocalPackage","createClient","WorkerChannelReporter","pMap","checkDocumentAvailability","exportDocuments","getDocumentCount","extractDocumentsFromNdjsonOrTarball","DOCUMENT_VALIDATION_TIMEOUT","getReferenceIds","isValidId","levelValues","MAX_VALIDATION_CONCURRENCY","REFERENCE_INTEGRITY_BATCH_SIZE","shouldIncludeDocument","clientConfig","dataset","level","maxCustomValidationConcurrency","maxFetchConcurrency","ndjsonFilePath","projectId","studioHost","workDir","workspace","workspaceName","Error","report","from","readerToGenerator","reader","done","value","read","main","process","exit","loadWorkspace","workspaces","resolvedDataset","resolvedProjectId","client","requestTagPrefix","config","apiVersion","event","loadedWorkspace","basePath","name","downloadFromExport","datasetName","projId","documentCount","loadedDocumentCount","response","body","getReader","downloadedCount","referencedIds","Set","documentIds","lines","createInterface","input","slugDate","Date","toISOString","replaceAll","toLowerCase","tempOutputFile","join","tmpdir","outputStream","createWriteStream","line","document","JSON","parse","add","_id","referenceId","write","stream","exportProgress","emit","Promise","resolve","reject","close","err","end","exportFinished","totalDocumentsToValidate","size","getDocuments","createReadStream","cleanup","promises","rm","downloadFromFile","filePath","undefined","checkReferenceExistence","_referencedIds","existingIds","idsToCheck","filter","id","has","toSorted","batches","i","length","push","slice","batch","omitted","omittedIds","item","reason","loadedReferenceIntegrity","cleanupDownloadedDocuments","getClient","options","withConfig","getDocumentExists","getLevel","markers","foundWarning","marker","validatedCount","validate","timeout","Symbol","validateDocument","result","race","environment","setTimeout","markerValue","flagLevelValue","info","errorMessage","message","intentUrl","encodeURIComponent","_type","validation","documentId","documentType","revision","_rev","concurrency"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/documents/validateDocuments.worker.ts"],"sourcesContent":["import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport readline from 'node:readline'\nimport {Readable} from 'node:stream'\nimport {workerData as _workerData, isMainThread, parentPort} from 'node:worker_threads'\n\nimport {getStudioWorkspaces, getWorkspace, resolveLocalPackage} from '@sanity/cli-core'\nimport {type ClientConfig, createClient, type SanityDocument} from '@sanity/client'\nimport {type ValidationContext, type ValidationMarker} from '@sanity/types'\nimport {WorkerChannelReporter} from '@sanity/worker-channels'\nimport pMap from 'p-map'\n\nimport {\n type AvailabilityResponse,\n checkDocumentAvailability,\n exportDocuments,\n getDocumentCount,\n} from '../../services/documents.js'\nimport {extractDocumentsFromNdjsonOrTarball} from '../../util/extractDocumentsFromNdjsonOrTarball.js'\nimport {\n DOCUMENT_VALIDATION_TIMEOUT,\n getReferenceIds,\n isValidId,\n levelValues,\n MAX_VALIDATION_CONCURRENCY,\n REFERENCE_INTEGRITY_BATCH_SIZE,\n shouldIncludeDocument,\n} from '../../util/validation/validateDocumentsUtils.js'\nimport {type ValidateDocumentsWorkerData, type ValidationWorkerChannel} from './types.js'\n\nconst {\n clientConfig,\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n studioHost,\n workDir,\n workspace: workspaceName,\n} = _workerData as ValidateDocumentsWorkerData\n\nif (isMainThread || !parentPort) {\n throw new Error('This module must be run as a worker thread')\n}\n\nconst report = WorkerChannelReporter.from<ValidationWorkerChannel>(parentPort)\n\n// eslint-disable-next-line n/no-unsupported-features/node-builtins\nasync function* readerToGenerator(reader: ReadableStreamDefaultReader<Uint8Array>) {\n while (true) {\n const {done, value} = await reader.read()\n if (value) yield value\n if (done) return\n }\n}\n\nawait main()\nprocess.exit()\n\nasync function loadWorkspace() {\n const workspaces = await getStudioWorkspaces(workDir)\n const workspace = getWorkspace(workspaces, workspaceName)\n\n const resolvedDataset = dataset || workspace.dataset\n const resolvedProjectId = projectId || workspace.projectId\n\n const client = createClient({\n ...clientConfig,\n dataset: resolvedDataset,\n projectId: resolvedProjectId,\n requestTagPrefix: 'sanity.cli.validate',\n }).config({apiVersion: 'v2021-03-25'})\n\n report.event.loadedWorkspace({\n basePath: workspace.basePath,\n dataset: workspace.dataset,\n name: workspace.name,\n projectId: workspace.projectId,\n })\n\n return {client, dataset: resolvedDataset, projectId: resolvedProjectId, workspace}\n}\n\ninterface DownloadFromExportOptions {\n dataset: string\n projectId: string\n}\n\nasync function downloadFromExport({\n dataset: datasetName,\n projectId: projId,\n}: DownloadFromExportOptions) {\n const documentCount = await getDocumentCount({dataset: datasetName, projectId: projId})\n report.event.loadedDocumentCount({documentCount})\n\n const response = await exportDocuments({dataset: datasetName, projectId: projId})\n\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Could not get reader from response body.')\n\n let downloadedCount = 0\n const referencedIds = new Set<string>()\n const documentIds = new Set<string>()\n const lines = readline.createInterface({input: Readable.from(readerToGenerator(reader))})\n\n // Note: we stream the export to a file and then re-read from that file to\n // make this less memory intensive.\n // this is a similar pattern to the import/export CLI commands\n const slugDate = new Date()\n .toISOString()\n .replaceAll(/[^a-z0-9]/gi, '-')\n .toLowerCase()\n const tempOutputFile = path.join(os.tmpdir(), `sanity-validate-${slugDate}.ndjson`)\n const outputStream = fs.createWriteStream(tempOutputFile)\n\n for await (const line of lines) {\n const document = JSON.parse(line) as SanityDocument\n\n if (shouldIncludeDocument(document)) {\n documentIds.add(document._id)\n for (const referenceId of getReferenceIds(document)) {\n referencedIds.add(referenceId)\n }\n\n outputStream.write(`${line}\\n`)\n }\n\n downloadedCount++\n report.stream.exportProgress.emit({documentCount, downloadedCount})\n }\n\n await new Promise<void>((resolve, reject) =>\n outputStream.close((err) => (err ? reject(err) : resolve())),\n )\n\n report.stream.exportProgress.end()\n report.event.exportFinished({totalDocumentsToValidate: documentIds.size})\n\n const getDocuments = () =>\n extractDocumentsFromNdjsonOrTarball(fs.createReadStream(tempOutputFile))\n\n return {cleanup: () => fs.promises.rm(tempOutputFile), documentIds, getDocuments, referencedIds}\n}\n\nasync function downloadFromFile(filePath: string) {\n const referencedIds = new Set<string>()\n const documentIds = new Set<string>()\n const getDocuments = () => extractDocumentsFromNdjsonOrTarball(fs.createReadStream(filePath))\n\n for await (const document of getDocuments()) {\n if (shouldIncludeDocument(document)) {\n documentIds.add(document._id)\n for (const referenceId of getReferenceIds(document)) {\n referencedIds.add(referenceId)\n }\n }\n }\n\n report.event.exportFinished({totalDocumentsToValidate: documentIds.size})\n\n return {cleanup: undefined, documentIds, getDocuments, referencedIds}\n}\n\ninterface CheckReferenceExistenceOptions {\n dataset: string\n documentIds: Set<string>\n projectId: string\n referencedIds: Set<string>\n}\n\nasync function checkReferenceExistence({\n dataset: datasetName,\n documentIds,\n projectId: projId,\n referencedIds: _referencedIds,\n}: CheckReferenceExistenceOptions) {\n const existingIds = new Set(documentIds)\n const idsToCheck = [..._referencedIds]\n .filter((id) => !existingIds.has(id) && isValidId(id))\n .toSorted()\n\n const batches: string[][] = []\n for (let i = 0; i < idsToCheck.length; i += REFERENCE_INTEGRITY_BATCH_SIZE) {\n batches.push(idsToCheck.slice(i, i + REFERENCE_INTEGRITY_BATCH_SIZE))\n }\n\n for (const batch of batches) {\n const {omitted} = await checkDocumentAvailability({\n dataset: datasetName,\n documentIds: batch,\n projectId: projId,\n })\n\n const omittedIds: Record<string, AvailabilityResponse['omitted'][number]['reason']> = {}\n for (const item of omitted) {\n omittedIds[item.id] = item.reason\n }\n\n for (const id of batch) {\n // unless the document ID is in the `omitted` object explictly due to\n // the reason `'existence'`, then it should exist\n if (omittedIds[id] !== 'existence') {\n existingIds.add(id)\n }\n }\n }\n report.event.loadedReferenceIntegrity()\n\n return {existingIds}\n}\n\nasync function main() {\n let cleanupDownloadedDocuments: (() => Promise<void>) | undefined\n\n try {\n const {client, dataset: datasetName, projectId: projId, workspace} = await loadWorkspace()\n const {cleanup, documentIds, getDocuments, referencedIds} = ndjsonFilePath\n ? await downloadFromFile(ndjsonFilePath)\n : await downloadFromExport({dataset: datasetName, projectId: projId})\n cleanupDownloadedDocuments = cleanup\n const {existingIds} = await checkReferenceExistence({\n dataset: datasetName,\n documentIds,\n projectId: projId,\n referencedIds,\n })\n\n const getClient = <TOptions extends Partial<ClientConfig>>(options: TOptions) =>\n client.withConfig(options)\n\n const getDocumentExists: ValidationContext['getDocumentExists'] = ({id}) =>\n Promise.resolve(existingIds.has(id))\n\n const getLevel = (markers: ValidationMarker[]) => {\n let foundWarning = false\n for (const marker of markers) {\n if (marker.level === 'error') return 'error'\n if (marker.level === 'warning') foundWarning = true\n }\n\n if (foundWarning) return 'warning'\n return 'info'\n }\n\n let validatedCount = 0\n\n const validate = async (document: SanityDocument) => {\n let markers: ValidationMarker[]\n\n try {\n const timeout = Symbol('timeout')\n\n const {validateDocument} = await resolveLocalPackage<typeof import('sanity')>(\n 'sanity',\n workDir,\n )\n\n const result = await Promise.race([\n validateDocument({\n document,\n environment: 'cli',\n // The local `sanity` package types its client against its own\n // @sanity/client major; the runtime surface validation uses is\n // compatible across v7/v8.\n getClient: getClient as unknown as Parameters<typeof validateDocument>[0]['getClient'],\n getDocumentExists,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n workspace,\n }),\n new Promise<typeof timeout>((resolve) =>\n setTimeout(() => resolve(timeout), DOCUMENT_VALIDATION_TIMEOUT),\n ),\n ])\n\n if (result === timeout) {\n throw new Error(\n `Document '${document._id}' failed to validate within ${DOCUMENT_VALIDATION_TIMEOUT}ms.`,\n )\n }\n\n markers = result\n // filter out unwanted levels\n .filter((marker) => {\n const markerValue = levelValues[marker.level]\n const flagLevelValue =\n levelValues[level as keyof typeof levelValues] ?? levelValues.info\n return markerValue <= flagLevelValue\n })\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error'\n\n const message = `Exception occurred while validating value: ${errorMessage}`\n\n markers = [\n {\n level: 'error',\n message,\n path: [],\n },\n ]\n }\n\n validatedCount++\n\n const intentUrl =\n studioHost &&\n `${studioHost}${path.resolve(\n workspace.basePath,\n `/intent/edit/id=${encodeURIComponent(document._id)};type=${encodeURIComponent(\n document._type,\n )}`,\n )}`\n\n report.stream.validation.emit({\n documentId: document._id,\n documentType: document._type,\n ...(intentUrl && {intentUrl}),\n level: getLevel(markers),\n markers,\n revision: document._rev,\n validatedCount,\n })\n }\n\n await pMap(getDocuments(), validate, {concurrency: MAX_VALIDATION_CONCURRENCY})\n\n report.stream.validation.end()\n } finally {\n await cleanupDownloadedDocuments?.()\n }\n}\n"],"names":["fs","os","path","readline","Readable","workerData","_workerData","isMainThread","parentPort","getStudioWorkspaces","getWorkspace","resolveLocalPackage","createClient","WorkerChannelReporter","pMap","checkDocumentAvailability","exportDocuments","getDocumentCount","extractDocumentsFromNdjsonOrTarball","DOCUMENT_VALIDATION_TIMEOUT","getReferenceIds","isValidId","levelValues","MAX_VALIDATION_CONCURRENCY","REFERENCE_INTEGRITY_BATCH_SIZE","shouldIncludeDocument","clientConfig","dataset","level","maxCustomValidationConcurrency","maxFetchConcurrency","ndjsonFilePath","projectId","studioHost","workDir","workspace","workspaceName","Error","report","from","readerToGenerator","reader","done","value","read","main","process","exit","loadWorkspace","workspaces","resolvedDataset","resolvedProjectId","client","requestTagPrefix","config","apiVersion","event","loadedWorkspace","basePath","name","downloadFromExport","datasetName","projId","documentCount","loadedDocumentCount","response","body","getReader","downloadedCount","referencedIds","Set","documentIds","lines","createInterface","input","slugDate","Date","toISOString","replaceAll","toLowerCase","tempOutputFile","join","tmpdir","outputStream","createWriteStream","line","document","JSON","parse","add","_id","referenceId","write","stream","exportProgress","emit","Promise","resolve","reject","close","err","end","exportFinished","totalDocumentsToValidate","size","getDocuments","createReadStream","cleanup","promises","rm","downloadFromFile","filePath","undefined","checkReferenceExistence","_referencedIds","existingIds","idsToCheck","filter","id","has","toSorted","batches","i","length","push","slice","batch","omitted","omittedIds","item","reason","loadedReferenceIntegrity","cleanupDownloadedDocuments","getClient","options","withConfig","getDocumentExists","getLevel","markers","foundWarning","marker","validatedCount","validate","timeout","Symbol","validateDocument","result","race","environment","setTimeout","markerValue","flagLevelValue","info","errorMessage","message","intentUrl","encodeURIComponent","_type","validation","documentId","documentType","revision","_rev","concurrency"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { styleText } from 'node:util';
2
2
  import { CLIError } from '@oclif/core/errors';
3
+ import { exitCodes } from '@sanity/cli-core';
3
4
  import { writeEnvVarsToFile } from './writeEnvVarsToFile.js';
4
5
  export async function createOrAppendEnvVars({ envVars, filename, framework, log, output, outputPath }) {
5
6
  try {
@@ -17,7 +18,7 @@ export async function createOrAppendEnvVars({ envVars, filename, framework, log,
17
18
  } catch (err) {
18
19
  output.error(err);
19
20
  throw new CLIError('An error occurred while creating .env', {
20
- exit: 1
21
+ exit: exitCodes.RUNTIME_ERROR
21
22
  });
22
23
  }
23
24
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/init/env/createOrAppendEnvVars.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {CLIError} from '@oclif/core/errors'\nimport {Output} from '@sanity/cli-core'\n\nimport {VersionedFramework} from '../types.js'\nimport {writeEnvVarsToFile} from './writeEnvVarsToFile.js'\n\ninterface CreateOrAppendEnvVarsOptions {\n envVars: Record<string, string>\n filename: string\n framework: VersionedFramework | null\n log: boolean\n output: Output\n outputPath: string\n}\n\nexport async function createOrAppendEnvVars({\n envVars,\n filename,\n framework,\n log,\n output,\n outputPath,\n}: CreateOrAppendEnvVarsOptions) {\n try {\n if (framework && framework.envPrefix && log) {\n output.log(\n `\\nDetected framework ${styleText('blue', framework?.name ?? '')}, using prefix '${\n framework.envPrefix\n }'`,\n )\n }\n\n await writeEnvVarsToFile({\n envVars,\n filename,\n framework,\n log,\n output,\n outputPath,\n })\n } catch (err) {\n output.error(err)\n throw new CLIError('An error occurred while creating .env', {exit: 1})\n }\n}\n"],"names":["styleText","CLIError","writeEnvVarsToFile","createOrAppendEnvVars","envVars","filename","framework","log","output","outputPath","envPrefix","name","err","error","exit"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,QAAQ,QAAO,qBAAoB;AAI3C,SAAQC,kBAAkB,QAAO,0BAAyB;AAW1D,OAAO,eAAeC,sBAAsB,EAC1CC,OAAO,EACPC,QAAQ,EACRC,SAAS,EACTC,GAAG,EACHC,MAAM,EACNC,UAAU,EACmB;IAC7B,IAAI;QACF,IAAIH,aAAaA,UAAUI,SAAS,IAAIH,KAAK;YAC3CC,OAAOD,GAAG,CACR,CAAC,qBAAqB,EAAEP,UAAU,QAAQM,WAAWK,QAAQ,IAAI,gBAAgB,EAC/EL,UAAUI,SAAS,CACpB,CAAC,CAAC;QAEP;QAEA,MAAMR,mBAAmB;YACvBE;YACAC;YACAC;YACAC;YACAC;YACAC;QACF;IACF,EAAE,OAAOG,KAAK;QACZJ,OAAOK,KAAK,CAACD;QACb,MAAM,IAAIX,SAAS,yCAAyC;YAACa,MAAM;QAAC;IACtE;AACF"}
1
+ {"version":3,"sources":["../../../../src/actions/init/env/createOrAppendEnvVars.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes, Output} from '@sanity/cli-core'\n\nimport {VersionedFramework} from '../types.js'\nimport {writeEnvVarsToFile} from './writeEnvVarsToFile.js'\n\ninterface CreateOrAppendEnvVarsOptions {\n envVars: Record<string, string>\n filename: string\n framework: VersionedFramework | null\n log: boolean\n output: Output\n outputPath: string\n}\n\nexport async function createOrAppendEnvVars({\n envVars,\n filename,\n framework,\n log,\n output,\n outputPath,\n}: CreateOrAppendEnvVarsOptions) {\n try {\n if (framework && framework.envPrefix && log) {\n output.log(\n `\\nDetected framework ${styleText('blue', framework?.name ?? '')}, using prefix '${\n framework.envPrefix\n }'`,\n )\n }\n\n await writeEnvVarsToFile({\n envVars,\n filename,\n framework,\n log,\n output,\n outputPath,\n })\n } catch (err) {\n output.error(err)\n throw new CLIError('An error occurred while creating .env', {exit: exitCodes.RUNTIME_ERROR})\n }\n}\n"],"names":["styleText","CLIError","exitCodes","writeEnvVarsToFile","createOrAppendEnvVars","envVars","filename","framework","log","output","outputPath","envPrefix","name","err","error","exit","RUNTIME_ERROR"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,SAAS,QAAe,mBAAkB;AAGlD,SAAQC,kBAAkB,QAAO,0BAAyB;AAW1D,OAAO,eAAeC,sBAAsB,EAC1CC,OAAO,EACPC,QAAQ,EACRC,SAAS,EACTC,GAAG,EACHC,MAAM,EACNC,UAAU,EACmB;IAC7B,IAAI;QACF,IAAIH,aAAaA,UAAUI,SAAS,IAAIH,KAAK;YAC3CC,OAAOD,GAAG,CACR,CAAC,qBAAqB,EAAER,UAAU,QAAQO,WAAWK,QAAQ,IAAI,gBAAgB,EAC/EL,UAAUI,SAAS,CACpB,CAAC,CAAC;QAEP;QAEA,MAAMR,mBAAmB;YACvBE;YACAC;YACAC;YACAC;YACAC;YACAC;QACF;IACF,EAAE,OAAOG,KAAK;QACZJ,OAAOK,KAAK,CAACD;QACb,MAAM,IAAIZ,SAAS,yCAAyC;YAACc,MAAMb,UAAUc,aAAa;QAAA;IAC5F;AACF"}
@@ -1,6 +1,6 @@
1
- import dotenv from 'dotenv';
1
+ import { parseEnv } from 'node:util';
2
2
  export function parseAndUpdateEnvVars({ envVars, fileContents, log, output }) {
3
- const existingKeys = dotenv.parse(fileContents) // this will contain all vars
3
+ const existingKeys = parseEnv(fileContents) // this will contain all vars
4
4
  ;
5
5
  const updatedKeys = {} // this will only contain our vars
6
6
  ;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/init/env/parseAndUpdateEnvVars.ts"],"sourcesContent":["import {Output} from '@sanity/cli-core'\nimport dotenv from 'dotenv'\n\ninterface ParseAndUpdateEnvVarsOptions {\n envVars: Record<string, string>\n fileContents: string\n log: boolean\n output: Output\n}\n\nexport function parseAndUpdateEnvVars({\n envVars,\n fileContents,\n log,\n output,\n}: ParseAndUpdateEnvVarsOptions): string {\n const existingKeys = dotenv.parse(fileContents) // this will contain all vars\n const updatedKeys: Record<string, string> = {} // this will only contain our vars\n\n // find and update keys\n for (const [key, value] of Object.entries(envVars)) {\n if (!existingKeys[key]) {\n updatedKeys[key] = value\n\n if (log) {\n output.log(`Appended ${key}=\"${envVars[key]}\"`)\n }\n\n continue\n }\n\n if (log) {\n output.log(`Found existing ${key}, replacing value.`)\n }\n updatedKeys[key] = value\n }\n\n // clone fileContents and replace existing keys by string matching\n let updatedEnv = fileContents\n for (const [key, value] of Object.entries(updatedKeys)) {\n if (existingKeys[key]) {\n const existingValue = existingKeys[key]\n\n updatedEnv = updatedEnv\n .split('\\n')\n .map((line) => {\n if (\n !line.trim().startsWith('#') && // ignore comments\n new RegExp(String.raw`(^\\s*${key})((: )|( *=))`).test(line) // match key\n ) {\n return line.replace(existingValue, value)\n }\n return line\n })\n .join('\\n')\n } else {\n updatedEnv = `${updatedEnv.trim()}\\n${key}=\"${value}\"`\n }\n }\n\n // if file is empty, add a newline\n return `${updatedEnv}${fileContents === '' ? '\\n' : ''}`\n}\n"],"names":["dotenv","parseAndUpdateEnvVars","envVars","fileContents","log","output","existingKeys","parse","updatedKeys","key","value","Object","entries","updatedEnv","existingValue","split","map","line","trim","startsWith","RegExp","String","raw","test","replace","join"],"mappings":"AACA,OAAOA,YAAY,SAAQ;AAS3B,OAAO,SAASC,sBAAsB,EACpCC,OAAO,EACPC,YAAY,EACZC,GAAG,EACHC,MAAM,EACuB;IAC7B,MAAMC,eAAeN,OAAOO,KAAK,CAACJ,cAAc,6BAA6B;;IAC7E,MAAMK,cAAsC,CAAC,EAAE,kCAAkC;;IAEjF,uBAAuB;IACvB,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACV,SAAU;QAClD,IAAI,CAACI,YAAY,CAACG,IAAI,EAAE;YACtBD,WAAW,CAACC,IAAI,GAAGC;YAEnB,IAAIN,KAAK;gBACPC,OAAOD,GAAG,CAAC,CAAC,SAAS,EAAEK,IAAI,EAAE,EAAEP,OAAO,CAACO,IAAI,CAAC,CAAC,CAAC;YAChD;YAEA;QACF;QAEA,IAAIL,KAAK;YACPC,OAAOD,GAAG,CAAC,CAAC,eAAe,EAAEK,IAAI,kBAAkB,CAAC;QACtD;QACAD,WAAW,CAACC,IAAI,GAAGC;IACrB;IAEA,kEAAkE;IAClE,IAAIG,aAAaV;IACjB,KAAK,MAAM,CAACM,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACJ,aAAc;QACtD,IAAIF,YAAY,CAACG,IAAI,EAAE;YACrB,MAAMK,gBAAgBR,YAAY,CAACG,IAAI;YAEvCI,aAAaA,WACVE,KAAK,CAAC,MACNC,GAAG,CAAC,CAACC;gBACJ,IACE,CAACA,KAAKC,IAAI,GAAGC,UAAU,CAAC,QAAQ,kBAAkB;gBAClD,IAAIC,OAAOC,OAAOC,GAAG,CAAC,KAAK,EAAEb,IAAI,aAAa,CAAC,EAAEc,IAAI,CAACN,MAAM,YAAY;kBACxE;oBACA,OAAOA,KAAKO,OAAO,CAACV,eAAeJ;gBACrC;gBACA,OAAOO;YACT,GACCQ,IAAI,CAAC;QACV,OAAO;YACLZ,aAAa,GAAGA,WAAWK,IAAI,GAAG,EAAE,EAAET,IAAI,EAAE,EAAEC,MAAM,CAAC,CAAC;QACxD;IACF;IAEA,kCAAkC;IAClC,OAAO,GAAGG,aAAaV,iBAAiB,KAAK,OAAO,IAAI;AAC1D"}
1
+ {"version":3,"sources":["../../../../src/actions/init/env/parseAndUpdateEnvVars.ts"],"sourcesContent":["import {parseEnv} from 'node:util'\n\nimport {Output} from '@sanity/cli-core'\n\ninterface ParseAndUpdateEnvVarsOptions {\n envVars: Record<string, string>\n fileContents: string\n log: boolean\n output: Output\n}\n\nexport function parseAndUpdateEnvVars({\n envVars,\n fileContents,\n log,\n output,\n}: ParseAndUpdateEnvVarsOptions): string {\n const existingKeys = parseEnv(fileContents) // this will contain all vars\n const updatedKeys: Record<string, string> = {} // this will only contain our vars\n\n // find and update keys\n for (const [key, value] of Object.entries(envVars)) {\n if (!existingKeys[key]) {\n updatedKeys[key] = value\n\n if (log) {\n output.log(`Appended ${key}=\"${envVars[key]}\"`)\n }\n\n continue\n }\n\n if (log) {\n output.log(`Found existing ${key}, replacing value.`)\n }\n updatedKeys[key] = value\n }\n\n // clone fileContents and replace existing keys by string matching\n let updatedEnv = fileContents\n for (const [key, value] of Object.entries(updatedKeys)) {\n if (existingKeys[key]) {\n const existingValue = existingKeys[key]\n\n updatedEnv = updatedEnv\n .split('\\n')\n .map((line) => {\n if (\n !line.trim().startsWith('#') && // ignore comments\n new RegExp(String.raw`(^\\s*${key})((: )|( *=))`).test(line) // match key\n ) {\n return line.replace(existingValue, value)\n }\n return line\n })\n .join('\\n')\n } else {\n updatedEnv = `${updatedEnv.trim()}\\n${key}=\"${value}\"`\n }\n }\n\n // if file is empty, add a newline\n return `${updatedEnv}${fileContents === '' ? '\\n' : ''}`\n}\n"],"names":["parseEnv","parseAndUpdateEnvVars","envVars","fileContents","log","output","existingKeys","updatedKeys","key","value","Object","entries","updatedEnv","existingValue","split","map","line","trim","startsWith","RegExp","String","raw","test","replace","join"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,YAAW;AAWlC,OAAO,SAASC,sBAAsB,EACpCC,OAAO,EACPC,YAAY,EACZC,GAAG,EACHC,MAAM,EACuB;IAC7B,MAAMC,eAAeN,SAASG,cAAc,6BAA6B;;IACzE,MAAMI,cAAsC,CAAC,EAAE,kCAAkC;;IAEjF,uBAAuB;IACvB,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACT,SAAU;QAClD,IAAI,CAACI,YAAY,CAACE,IAAI,EAAE;YACtBD,WAAW,CAACC,IAAI,GAAGC;YAEnB,IAAIL,KAAK;gBACPC,OAAOD,GAAG,CAAC,CAAC,SAAS,EAAEI,IAAI,EAAE,EAAEN,OAAO,CAACM,IAAI,CAAC,CAAC,CAAC;YAChD;YAEA;QACF;QAEA,IAAIJ,KAAK;YACPC,OAAOD,GAAG,CAAC,CAAC,eAAe,EAAEI,IAAI,kBAAkB,CAAC;QACtD;QACAD,WAAW,CAACC,IAAI,GAAGC;IACrB;IAEA,kEAAkE;IAClE,IAAIG,aAAaT;IACjB,KAAK,MAAM,CAACK,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACJ,aAAc;QACtD,IAAID,YAAY,CAACE,IAAI,EAAE;YACrB,MAAMK,gBAAgBP,YAAY,CAACE,IAAI;YAEvCI,aAAaA,WACVE,KAAK,CAAC,MACNC,GAAG,CAAC,CAACC;gBACJ,IACE,CAACA,KAAKC,IAAI,GAAGC,UAAU,CAAC,QAAQ,kBAAkB;gBAClD,IAAIC,OAAOC,OAAOC,GAAG,CAAC,KAAK,EAAEb,IAAI,aAAa,CAAC,EAAEc,IAAI,CAACN,MAAM,YAAY;kBACxE;oBACA,OAAOA,KAAKO,OAAO,CAACV,eAAeJ;gBACrC;gBACA,OAAOO;YACT,GACCQ,IAAI,CAAC;QACV,OAAO;YACLZ,aAAa,GAAGA,WAAWK,IAAI,GAAG,EAAE,EAAET,IAAI,EAAE,EAAEC,MAAM,CAAC,CAAC;QACxD;IACF;IAEA,kCAAkC;IAClC,OAAO,GAAGG,aAAaT,iBAAiB,KAAK,OAAO,IAAI;AAC1D"}
@@ -1,6 +1,6 @@
1
1
  import { exit } from '@oclif/core/errors';
2
2
  import { formatSchemaValidation, runSchemaExtraction, SchemaExtractedTrace, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
3
- import { getCliTelemetry } from '@sanity/cli-core';
3
+ import { exitCodes, getCliTelemetry } from '@sanity/cli-core';
4
4
  import { spinner } from '@sanity/cli-core/ux';
5
5
  import { schemasExtractDebug } from './utils/debug.js';
6
6
  export async function extractSchema(options) {
@@ -32,7 +32,7 @@ export async function extractSchema(options) {
32
32
  exit(1);
33
33
  }
34
34
  output.error(err instanceof Error ? err.message : 'Failed to extract schema', {
35
- exit: 1
35
+ exit: exitCodes.RUNTIME_ERROR
36
36
  });
37
37
  }
38
38
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/extractSchema.ts"],"sourcesContent":["import {exit} from '@oclif/core/errors'\nimport {\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractedTrace,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {getCliTelemetry, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\ninterface ExtractSchemaActionOptions {\n extractOptions: ExtractOptions\n output: Output\n}\n\nexport async function extractSchema(options: ExtractSchemaActionOptions): Promise<void> {\n const {extractOptions, output} = options\n const {enforceRequiredFields, format, outputPath} = extractOptions\n\n const spin = spinner(\n enforceRequiredFields ? 'Extracting schema with enforced required fields' : 'Extracting schema',\n ).start()\n\n const trace = getCliTelemetry().trace(SchemaExtractedTrace)\n trace.start()\n\n try {\n spin.text = `Writing schema to ${outputPath}`\n\n const schema = await runSchemaExtraction(extractOptions)\n\n trace.log({\n enforceRequiredFields,\n schemaAllTypesCount: schema.length,\n schemaDocumentTypesCount: schema.filter((type) => type.type === 'document').length,\n schemaFormat: format,\n schemaTypesCount: schema.filter((type) => type.type === 'type').length,\n })\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n trace.complete()\n } catch (err) {\n trace.error(err)\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail(\n enforceRequiredFields\n ? 'Failed to extract schema with enforced required fields'\n : 'Failed to extract schema',\n )\n\n // Display validation errors if available and exit\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n exit(1)\n }\n\n output.error(err instanceof Error ? err.message : 'Failed to extract schema', {exit: 1})\n }\n}\n"],"names":["exit","formatSchemaValidation","runSchemaExtraction","SchemaExtractedTrace","SchemaExtractionError","getCliTelemetry","spinner","schemasExtractDebug","extractSchema","options","extractOptions","output","enforceRequiredFields","format","outputPath","spin","start","trace","text","schema","log","schemaAllTypesCount","length","schemaDocumentTypesCount","filter","type","schemaFormat","schemaTypesCount","succeed","complete","err","error","fail","validation","Error","message"],"mappings":"AAAA,SAAQA,IAAI,QAAO,qBAAoB;AACvC,SAEEC,sBAAsB,EACtBC,mBAAmB,EACnBC,oBAAoB,EACpBC,qBAAqB,QAChB,sCAAqC;AAC5C,SAAQC,eAAe,QAAoB,mBAAkB;AAC7D,SAAQC,OAAO,QAAO,sBAAqB;AAE3C,SAAQC,mBAAmB,QAAO,mBAAkB;AAOpD,OAAO,eAAeC,cAAcC,OAAmC;IACrE,MAAM,EAACC,cAAc,EAAEC,MAAM,EAAC,GAAGF;IACjC,MAAM,EAACG,qBAAqB,EAAEC,MAAM,EAAEC,UAAU,EAAC,GAAGJ;IAEpD,MAAMK,OAAOT,QACXM,wBAAwB,oDAAoD,qBAC5EI,KAAK;IAEP,MAAMC,QAAQZ,kBAAkBY,KAAK,CAACd;IACtCc,MAAMD,KAAK;IAEX,IAAI;QACFD,KAAKG,IAAI,GAAG,CAAC,kBAAkB,EAAEJ,YAAY;QAE7C,MAAMK,SAAS,MAAMjB,oBAAoBQ;QAEzCO,MAAMG,GAAG,CAAC;YACRR;YACAS,qBAAqBF,OAAOG,MAAM;YAClCC,0BAA0BJ,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,YAAYH,MAAM;YAClFI,cAAcb;YACdc,kBAAkBR,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,QAAQH,MAAM;QACxE;QAEAP,KAAKa,OAAO,CACVhB,wBACI,CAAC,oBAAoB,EAAEE,WAAW,8BAA8B,CAAC,GACjE,CAAC,oBAAoB,EAAEA,YAAY;QAGzCG,MAAMY,QAAQ;IAChB,EAAE,OAAOC,KAAK;QACZb,MAAMc,KAAK,CAACD;QACZvB,oBAAoB,4BAA4BuB;QAChDf,KAAKiB,IAAI,CACPpB,wBACI,2DACA;QAGN,kDAAkD;QAClD,IAAIkB,eAAe1B,yBAAyB0B,IAAIG,UAAU,IAAIH,IAAIG,UAAU,CAACX,MAAM,GAAG,GAAG;YACvFX,OAAOS,GAAG,CAAC;YACXT,OAAOS,GAAG,CAACnB,uBAAuB6B,IAAIG,UAAU;YAChDjC,KAAK;QACP;QAEAW,OAAOoB,KAAK,CAACD,eAAeI,QAAQJ,IAAIK,OAAO,GAAG,4BAA4B;YAACnC,MAAM;QAAC;IACxF;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/schema/extractSchema.ts"],"sourcesContent":["import {exit} from '@oclif/core/errors'\nimport {\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractedTrace,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes, getCliTelemetry, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\ninterface ExtractSchemaActionOptions {\n extractOptions: ExtractOptions\n output: Output\n}\n\nexport async function extractSchema(options: ExtractSchemaActionOptions): Promise<void> {\n const {extractOptions, output} = options\n const {enforceRequiredFields, format, outputPath} = extractOptions\n\n const spin = spinner(\n enforceRequiredFields ? 'Extracting schema with enforced required fields' : 'Extracting schema',\n ).start()\n\n const trace = getCliTelemetry().trace(SchemaExtractedTrace)\n trace.start()\n\n try {\n spin.text = `Writing schema to ${outputPath}`\n\n const schema = await runSchemaExtraction(extractOptions)\n\n trace.log({\n enforceRequiredFields,\n schemaAllTypesCount: schema.length,\n schemaDocumentTypesCount: schema.filter((type) => type.type === 'document').length,\n schemaFormat: format,\n schemaTypesCount: schema.filter((type) => type.type === 'type').length,\n })\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n trace.complete()\n } catch (err) {\n trace.error(err)\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail(\n enforceRequiredFields\n ? 'Failed to extract schema with enforced required fields'\n : 'Failed to extract schema',\n )\n\n // Display validation errors if available and exit\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n exit(1)\n }\n\n output.error(err instanceof Error ? err.message : 'Failed to extract schema', {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n}\n"],"names":["exit","formatSchemaValidation","runSchemaExtraction","SchemaExtractedTrace","SchemaExtractionError","exitCodes","getCliTelemetry","spinner","schemasExtractDebug","extractSchema","options","extractOptions","output","enforceRequiredFields","format","outputPath","spin","start","trace","text","schema","log","schemaAllTypesCount","length","schemaDocumentTypesCount","filter","type","schemaFormat","schemaTypesCount","succeed","complete","err","error","fail","validation","Error","message","RUNTIME_ERROR"],"mappings":"AAAA,SAAQA,IAAI,QAAO,qBAAoB;AACvC,SAEEC,sBAAsB,EACtBC,mBAAmB,EACnBC,oBAAoB,EACpBC,qBAAqB,QAChB,sCAAqC;AAC5C,SAAQC,SAAS,EAAEC,eAAe,QAAoB,mBAAkB;AACxE,SAAQC,OAAO,QAAO,sBAAqB;AAE3C,SAAQC,mBAAmB,QAAO,mBAAkB;AAOpD,OAAO,eAAeC,cAAcC,OAAmC;IACrE,MAAM,EAACC,cAAc,EAAEC,MAAM,EAAC,GAAGF;IACjC,MAAM,EAACG,qBAAqB,EAAEC,MAAM,EAAEC,UAAU,EAAC,GAAGJ;IAEpD,MAAMK,OAAOT,QACXM,wBAAwB,oDAAoD,qBAC5EI,KAAK;IAEP,MAAMC,QAAQZ,kBAAkBY,KAAK,CAACf;IACtCe,MAAMD,KAAK;IAEX,IAAI;QACFD,KAAKG,IAAI,GAAG,CAAC,kBAAkB,EAAEJ,YAAY;QAE7C,MAAMK,SAAS,MAAMlB,oBAAoBS;QAEzCO,MAAMG,GAAG,CAAC;YACRR;YACAS,qBAAqBF,OAAOG,MAAM;YAClCC,0BAA0BJ,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,YAAYH,MAAM;YAClFI,cAAcb;YACdc,kBAAkBR,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,QAAQH,MAAM;QACxE;QAEAP,KAAKa,OAAO,CACVhB,wBACI,CAAC,oBAAoB,EAAEE,WAAW,8BAA8B,CAAC,GACjE,CAAC,oBAAoB,EAAEA,YAAY;QAGzCG,MAAMY,QAAQ;IAChB,EAAE,OAAOC,KAAK;QACZb,MAAMc,KAAK,CAACD;QACZvB,oBAAoB,4BAA4BuB;QAChDf,KAAKiB,IAAI,CACPpB,wBACI,2DACA;QAGN,kDAAkD;QAClD,IAAIkB,eAAe3B,yBAAyB2B,IAAIG,UAAU,IAAIH,IAAIG,UAAU,CAACX,MAAM,GAAG,GAAG;YACvFX,OAAOS,GAAG,CAAC;YACXT,OAAOS,GAAG,CAACpB,uBAAuB8B,IAAIG,UAAU;YAChDlC,KAAK;QACP;QAEAY,OAAOoB,KAAK,CAACD,eAAeI,QAAQJ,IAAIK,OAAO,GAAG,4BAA4B;YAC5EpC,MAAMK,UAAUgC,aAAa;QAC/B;IACF;AACF"}
@@ -1,5 +1,6 @@
1
1
  import { dirname, isAbsolute, join, relative } from 'node:path';
2
2
  import { createSchemaPatternMatcher, formatSchemaValidation, runSchemaExtraction, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
3
+ import { exitCodes } from '@sanity/cli-core';
3
4
  import { spinner } from '@sanity/cli-core/ux';
4
5
  import { watch as chokidarWatch } from 'chokidar';
5
6
  import debounce from 'lodash-es/debounce.js';
@@ -81,7 +82,7 @@ import { schemasExtractDebug } from './utils/debug.js';
81
82
  output.log(formatSchemaValidation(err.validation));
82
83
  } else if (err instanceof Error) {
83
84
  output.error(err.message, {
84
- exit: 1
85
+ exit: exitCodes.RUNTIME_ERROR
85
86
  });
86
87
  }
87
88
  return false;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/extractSchemaWatcher.ts"],"sourcesContent":["import {dirname, isAbsolute, join, relative} from 'node:path'\n\nimport {\n createSchemaPatternMatcher,\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {watch as chokidarWatch, type FSWatcher} from 'chokidar'\nimport debounce from 'lodash-es/debounce.js'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\n/** Default glob patterns to watch for schema changes */\nexport const DEFAULT_WATCH_PATTERNS = [\n 'sanity.config.{js,jsx,ts,tsx,mjs}',\n 'schema*/**/*.{js,jsx,ts,tsx,mjs}',\n]\n\n/** Default patterns to ignore when watching */\nconst IGNORED_PATTERNS = [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/lib/**',\n '**/.sanity/**',\n]\n\ninterface ExtractSchemaWatcherOptions {\n extractOptions: ExtractOptions\n output: Output\n watchPatterns: string[]\n\n onExtraction?: (result: {duration: number; success: boolean}) => void\n}\n\ninterface ExtractSchemaWatcher {\n close: () => Promise<void>\n watcher: FSWatcher\n}\n\n/** State for tracking extraction status */\ninterface WatchState {\n isExtracting: boolean\n pendingExtraction: boolean\n}\n\n/** Return type for createExtractionRunner */\ninterface ExtractionRunner {\n runExtraction: () => Promise<void>\n state: WatchState\n}\n\n/**\n * Creates an extraction runner with concurrency control.\n * If extraction is already running, queues one more extraction to run after completion.\n * Multiple queued requests are coalesced into a single pending extraction.\n */\nfunction createExtractionRunner(onExtract: () => Promise<void>): ExtractionRunner {\n const state: WatchState = {\n isExtracting: false,\n pendingExtraction: false,\n }\n\n async function runExtraction(): Promise<void> {\n if (state.isExtracting) {\n state.pendingExtraction = true\n return\n }\n\n state.isExtracting = true\n state.pendingExtraction = false\n\n try {\n await onExtract()\n } finally {\n state.isExtracting = false\n\n // If a change came in during extraction, run again\n if (state.pendingExtraction) {\n state.pendingExtraction = false\n await runExtraction()\n }\n }\n }\n\n return {runExtraction, state}\n}\n\n/**\n * Starts a schema watcher that extracts schema on file changes.\n * Returns a watcher instance and a stop function.\n */\nexport async function startExtractSchemaWatcher(\n options: ExtractSchemaWatcherOptions,\n): Promise<ExtractSchemaWatcher> {\n const {extractOptions, onExtraction, output, watchPatterns} = options\n\n const {configPath, enforceRequiredFields, outputPath} = extractOptions\n const workDir = dirname(configPath)\n\n // Helper function to run extraction with spinner and error handling\n const runExtraction = async (): Promise<boolean> => {\n const spin = spinner(\n enforceRequiredFields\n ? 'Extracting schema with enforced required fields'\n : 'Extracting schema...',\n ).start()\n const extractionStartTime = Date.now()\n\n try {\n await runSchemaExtraction(extractOptions)\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: true})\n\n return true\n } catch (err) {\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: false})\n\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail('Extraction failed')\n\n // Display validation errors if available\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n } else if (err instanceof Error) {\n output.error(err.message, {exit: 1})\n }\n\n return false\n }\n }\n\n // Run initial extraction\n await runExtraction()\n\n const absoluteWatchPatterns = watchPatterns.map((pattern) => join(workDir, pattern))\n\n // Create extraction runner with concurrency control\n const {runExtraction: runConcurrentExtraction} = createExtractionRunner(async () => {\n await runExtraction()\n })\n\n // Debounced extraction trigger (1 second delay)\n const debouncedExtract = debounce(() => {\n void runConcurrentExtraction()\n }, 1000)\n\n const {isMatch} = createSchemaPatternMatcher(watchPatterns)\n\n const watcher: FSWatcher = chokidarWatch(absoluteWatchPatterns, {\n cwd: workDir,\n ignored: IGNORED_PATTERNS,\n ignoreInitial: true,\n })\n\n watcher.on('all', (event, filePath) => {\n if (!isMatch(filePath, workDir)) {\n return\n }\n\n const timestamp = new Date().toLocaleTimeString()\n const relativePath = isAbsolute(filePath) ? relative(workDir, filePath) : filePath\n output.log(`[${timestamp}] ${event}: ${relativePath}`)\n debouncedExtract()\n })\n\n watcher.on('error', (err) => {\n output.error(`Watcher error: ${err instanceof Error ? err.message : String(err)}`)\n })\n\n return {\n close: () => watcher.close(),\n watcher,\n }\n}\n"],"names":["dirname","isAbsolute","join","relative","createSchemaPatternMatcher","formatSchemaValidation","runSchemaExtraction","SchemaExtractionError","spinner","watch","chokidarWatch","debounce","schemasExtractDebug","DEFAULT_WATCH_PATTERNS","IGNORED_PATTERNS","createExtractionRunner","onExtract","state","isExtracting","pendingExtraction","runExtraction","startExtractSchemaWatcher","options","extractOptions","onExtraction","output","watchPatterns","configPath","enforceRequiredFields","outputPath","workDir","spin","start","extractionStartTime","Date","now","succeed","duration","success","err","fail","validation","length","log","Error","error","message","exit","absoluteWatchPatterns","map","pattern","runConcurrentExtraction","debouncedExtract","isMatch","watcher","cwd","ignored","ignoreInitial","on","event","filePath","timestamp","toLocaleTimeString","relativePath","String","close"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/schema/extractSchemaWatcher.ts"],"sourcesContent":["import {dirname, isAbsolute, join, relative} from 'node:path'\n\nimport {\n createSchemaPatternMatcher,\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {watch as chokidarWatch, type FSWatcher} from 'chokidar'\nimport debounce from 'lodash-es/debounce.js'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\n/** Default glob patterns to watch for schema changes */\nexport const DEFAULT_WATCH_PATTERNS = [\n 'sanity.config.{js,jsx,ts,tsx,mjs}',\n 'schema*/**/*.{js,jsx,ts,tsx,mjs}',\n]\n\n/** Default patterns to ignore when watching */\nconst IGNORED_PATTERNS = [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/lib/**',\n '**/.sanity/**',\n]\n\ninterface ExtractSchemaWatcherOptions {\n extractOptions: ExtractOptions\n output: Output\n watchPatterns: string[]\n\n onExtraction?: (result: {duration: number; success: boolean}) => void\n}\n\ninterface ExtractSchemaWatcher {\n close: () => Promise<void>\n watcher: FSWatcher\n}\n\n/** State for tracking extraction status */\ninterface WatchState {\n isExtracting: boolean\n pendingExtraction: boolean\n}\n\n/** Return type for createExtractionRunner */\ninterface ExtractionRunner {\n runExtraction: () => Promise<void>\n state: WatchState\n}\n\n/**\n * Creates an extraction runner with concurrency control.\n * If extraction is already running, queues one more extraction to run after completion.\n * Multiple queued requests are coalesced into a single pending extraction.\n */\nfunction createExtractionRunner(onExtract: () => Promise<void>): ExtractionRunner {\n const state: WatchState = {\n isExtracting: false,\n pendingExtraction: false,\n }\n\n async function runExtraction(): Promise<void> {\n if (state.isExtracting) {\n state.pendingExtraction = true\n return\n }\n\n state.isExtracting = true\n state.pendingExtraction = false\n\n try {\n await onExtract()\n } finally {\n state.isExtracting = false\n\n // If a change came in during extraction, run again\n if (state.pendingExtraction) {\n state.pendingExtraction = false\n await runExtraction()\n }\n }\n }\n\n return {runExtraction, state}\n}\n\n/**\n * Starts a schema watcher that extracts schema on file changes.\n * Returns a watcher instance and a stop function.\n */\nexport async function startExtractSchemaWatcher(\n options: ExtractSchemaWatcherOptions,\n): Promise<ExtractSchemaWatcher> {\n const {extractOptions, onExtraction, output, watchPatterns} = options\n\n const {configPath, enforceRequiredFields, outputPath} = extractOptions\n const workDir = dirname(configPath)\n\n // Helper function to run extraction with spinner and error handling\n const runExtraction = async (): Promise<boolean> => {\n const spin = spinner(\n enforceRequiredFields\n ? 'Extracting schema with enforced required fields'\n : 'Extracting schema...',\n ).start()\n const extractionStartTime = Date.now()\n\n try {\n await runSchemaExtraction(extractOptions)\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: true})\n\n return true\n } catch (err) {\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: false})\n\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail('Extraction failed')\n\n // Display validation errors if available\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n } else if (err instanceof Error) {\n output.error(err.message, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n return false\n }\n }\n\n // Run initial extraction\n await runExtraction()\n\n const absoluteWatchPatterns = watchPatterns.map((pattern) => join(workDir, pattern))\n\n // Create extraction runner with concurrency control\n const {runExtraction: runConcurrentExtraction} = createExtractionRunner(async () => {\n await runExtraction()\n })\n\n // Debounced extraction trigger (1 second delay)\n const debouncedExtract = debounce(() => {\n void runConcurrentExtraction()\n }, 1000)\n\n const {isMatch} = createSchemaPatternMatcher(watchPatterns)\n\n const watcher: FSWatcher = chokidarWatch(absoluteWatchPatterns, {\n cwd: workDir,\n ignored: IGNORED_PATTERNS,\n ignoreInitial: true,\n })\n\n watcher.on('all', (event, filePath) => {\n if (!isMatch(filePath, workDir)) {\n return\n }\n\n const timestamp = new Date().toLocaleTimeString()\n const relativePath = isAbsolute(filePath) ? relative(workDir, filePath) : filePath\n output.log(`[${timestamp}] ${event}: ${relativePath}`)\n debouncedExtract()\n })\n\n watcher.on('error', (err) => {\n output.error(`Watcher error: ${err instanceof Error ? err.message : String(err)}`)\n })\n\n return {\n close: () => watcher.close(),\n watcher,\n }\n}\n"],"names":["dirname","isAbsolute","join","relative","createSchemaPatternMatcher","formatSchemaValidation","runSchemaExtraction","SchemaExtractionError","exitCodes","spinner","watch","chokidarWatch","debounce","schemasExtractDebug","DEFAULT_WATCH_PATTERNS","IGNORED_PATTERNS","createExtractionRunner","onExtract","state","isExtracting","pendingExtraction","runExtraction","startExtractSchemaWatcher","options","extractOptions","onExtraction","output","watchPatterns","configPath","enforceRequiredFields","outputPath","workDir","spin","start","extractionStartTime","Date","now","succeed","duration","success","err","fail","validation","length","log","Error","error","message","exit","RUNTIME_ERROR","absoluteWatchPatterns","map","pattern","runConcurrentExtraction","debouncedExtract","isMatch","watcher","cwd","ignored","ignoreInitial","on","event","filePath","timestamp","toLocaleTimeString","relativePath","String","close"],"mappings":"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"}
@@ -37,6 +37,9 @@ const debug = subdebug('uploadSchemaToLexicon');
37
37
  dataset: workspace.dataset,
38
38
  projectId: workspace.projectId
39
39
  });
40
+ // The local `sanity` package types its client against its own
41
+ // @sanity/client major; the runtime surface uploadSchema uses is
42
+ // compatible across v7/v8.
40
43
  const descriptorId = await uploadSchema(workspace.schema, workspaceClient);
41
44
  if (!descriptorId) {
42
45
  throw new Error(`Failed to get schema descriptor ID for workspace "${workspace.name}": upload returned empty result`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/uploadSchemaToLexicon.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {ux} from '@oclif/core/ux'\nimport {\n doImport,\n getLocalPackageVersion,\n getProjectCliClient,\n resolveLocalPackage,\n subdebug,\n} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {type StudioManifest, type Workspace} from 'sanity'\n\nimport {SCHEMA_API_VERSION} from '../../services/schemas.js'\n\nconst iconResolverPath = new URL('../manifest/iconResolver.js', import.meta.url).href\n\ninterface UploadSchemaToLexiconOptions {\n projectId: string\n workDir: string\n workspaces: Workspace[]\n\n verbose?: boolean\n}\n\nconst debug = subdebug('uploadSchemaToLexicon')\n\n/**\n * Uploads the schemas to Lexicon and returns the studio manifest\n * @param options - The options for the uploadSchemaToLexicon function\n * @returns The studio manifest\n */\nexport async function uploadSchemaToLexicon(\n options: UploadSchemaToLexiconOptions,\n): Promise<StudioManifest | null> {\n const {projectId, verbose, workDir, workspaces} = options\n const spin = spinner('Generating studio manifest').start()\n\n try {\n const schemaDescriptors = new Map<string, string>()\n\n const client = await getProjectCliClient({\n apiVersion: SCHEMA_API_VERSION,\n projectId,\n requestTagPrefix: 'sanity.cli.deploy',\n requireUser: true,\n })\n\n const [bundleVersion, {generateStudioManifest, uploadSchema}] = await Promise.all([\n getLocalPackageVersion('sanity', workDir),\n resolveLocalPackage<typeof import('sanity')>('sanity', workDir),\n ])\n\n if (!bundleVersion) {\n throw new Error('Failed to find sanity version')\n }\n\n for (const workspace of workspaces) {\n const workspaceClient = client.withConfig({\n dataset: workspace.dataset,\n projectId: workspace.projectId,\n })\n\n try {\n debug('Uploading schema to lexicon for workspace %o', {\n dataset: workspace.dataset,\n projectId: workspace.projectId,\n })\n const descriptorId = await uploadSchema(workspace.schema, workspaceClient)\n\n if (!descriptorId) {\n throw new Error(\n `Failed to get schema descriptor ID for workspace \"${workspace.name}\": upload returned empty result`,\n )\n }\n\n schemaDescriptors.set(workspace.name, descriptorId)\n debug(\n `Uploaded schema for workspace \"${workspace.name}\" to Lexicon with descriptor ID: ${descriptorId}`,\n )\n } catch (error) {\n debug('Error uploading schema to lexicon for workspace %o', error)\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n throw new Error(\n `Failed to upload schema for workspace \"${workspace.name}\": ${errorMessage}`,\n {cause: error},\n )\n }\n }\n\n // Lazy import to avoid pulling in @sanity/ui at module load time\n const {resolveIcon} = await doImport(iconResolverPath)\n\n // Generate studio manifest using the shared utility\n const manifest = await generateStudioManifest({\n buildId: JSON.stringify(Date.now()),\n bundleVersion,\n // @todo replace with import from @sanity/schema/_internal in future\n resolveIcon: async (workspace) =>\n (await resolveIcon({\n icon: workspace.icon,\n subtitle: workspace.subtitle,\n title: workspace.title || workspace.name || 'default',\n workDir,\n })) ?? undefined,\n resolveSchemaDescriptorId: (workspace) => schemaDescriptors.get(workspace.name),\n workspaces,\n })\n\n spin.succeed('Generated studio manifest')\n\n const studioManifest = manifest.workspaces.length === 0 ? null : manifest\n\n if (verbose) {\n if (studioManifest) {\n for (const workspace of studioManifest.workspaces) {\n ux.stdout(\n styleText(\n 'gray',\n `↳ projectId: ${workspace.projectId}, dataset: ${workspace.dataset}, schemaDescriptorId: ${workspace.schemaDescriptorId}`,\n ),\n )\n }\n } else {\n ux.stdout(`${styleText('gray', '↳ No workspaces found')}`)\n }\n }\n\n return studioManifest\n } catch (error) {\n spin.fail(error instanceof Error ? error.message : 'Unknown error')\n throw error\n }\n}\n"],"names":["styleText","ux","doImport","getLocalPackageVersion","getProjectCliClient","resolveLocalPackage","subdebug","spinner","SCHEMA_API_VERSION","iconResolverPath","URL","url","href","debug","uploadSchemaToLexicon","options","projectId","verbose","workDir","workspaces","spin","start","schemaDescriptors","Map","client","apiVersion","requestTagPrefix","requireUser","bundleVersion","generateStudioManifest","uploadSchema","Promise","all","Error","workspace","workspaceClient","withConfig","dataset","descriptorId","schema","name","set","error","errorMessage","message","cause","resolveIcon","manifest","buildId","JSON","stringify","Date","now","icon","subtitle","title","undefined","resolveSchemaDescriptorId","get","succeed","studioManifest","length","stdout","schemaDescriptorId","fail"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/schema/uploadSchemaToLexicon.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {ux} from '@oclif/core/ux'\nimport {\n doImport,\n getLocalPackageVersion,\n getProjectCliClient,\n resolveLocalPackage,\n subdebug,\n} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {type StudioManifest, type Workspace} from 'sanity'\n\nimport {SCHEMA_API_VERSION} from '../../services/schemas.js'\n\nconst iconResolverPath = new URL('../manifest/iconResolver.js', import.meta.url).href\n\ninterface UploadSchemaToLexiconOptions {\n projectId: string\n workDir: string\n workspaces: Workspace[]\n\n verbose?: boolean\n}\n\nconst debug = subdebug('uploadSchemaToLexicon')\n\n/**\n * Uploads the schemas to Lexicon and returns the studio manifest\n * @param options - The options for the uploadSchemaToLexicon function\n * @returns The studio manifest\n */\nexport async function uploadSchemaToLexicon(\n options: UploadSchemaToLexiconOptions,\n): Promise<StudioManifest | null> {\n const {projectId, verbose, workDir, workspaces} = options\n const spin = spinner('Generating studio manifest').start()\n\n try {\n const schemaDescriptors = new Map<string, string>()\n\n const client = await getProjectCliClient({\n apiVersion: SCHEMA_API_VERSION,\n projectId,\n requestTagPrefix: 'sanity.cli.deploy',\n requireUser: true,\n })\n\n const [bundleVersion, {generateStudioManifest, uploadSchema}] = await Promise.all([\n getLocalPackageVersion('sanity', workDir),\n resolveLocalPackage<typeof import('sanity')>('sanity', workDir),\n ])\n\n if (!bundleVersion) {\n throw new Error('Failed to find sanity version')\n }\n\n for (const workspace of workspaces) {\n const workspaceClient = client.withConfig({\n dataset: workspace.dataset,\n projectId: workspace.projectId,\n })\n\n try {\n debug('Uploading schema to lexicon for workspace %o', {\n dataset: workspace.dataset,\n projectId: workspace.projectId,\n })\n // The local `sanity` package types its client against its own\n // @sanity/client major; the runtime surface uploadSchema uses is\n // compatible across v7/v8.\n const descriptorId = await uploadSchema(\n workspace.schema,\n workspaceClient as unknown as Parameters<typeof uploadSchema>[1],\n )\n\n if (!descriptorId) {\n throw new Error(\n `Failed to get schema descriptor ID for workspace \"${workspace.name}\": upload returned empty result`,\n )\n }\n\n schemaDescriptors.set(workspace.name, descriptorId)\n debug(\n `Uploaded schema for workspace \"${workspace.name}\" to Lexicon with descriptor ID: ${descriptorId}`,\n )\n } catch (error) {\n debug('Error uploading schema to lexicon for workspace %o', error)\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n throw new Error(\n `Failed to upload schema for workspace \"${workspace.name}\": ${errorMessage}`,\n {cause: error},\n )\n }\n }\n\n // Lazy import to avoid pulling in @sanity/ui at module load time\n const {resolveIcon} = await doImport(iconResolverPath)\n\n // Generate studio manifest using the shared utility\n const manifest = await generateStudioManifest({\n buildId: JSON.stringify(Date.now()),\n bundleVersion,\n // @todo replace with import from @sanity/schema/_internal in future\n resolveIcon: async (workspace) =>\n (await resolveIcon({\n icon: workspace.icon,\n subtitle: workspace.subtitle,\n title: workspace.title || workspace.name || 'default',\n workDir,\n })) ?? undefined,\n resolveSchemaDescriptorId: (workspace) => schemaDescriptors.get(workspace.name),\n workspaces,\n })\n\n spin.succeed('Generated studio manifest')\n\n const studioManifest = manifest.workspaces.length === 0 ? null : manifest\n\n if (verbose) {\n if (studioManifest) {\n for (const workspace of studioManifest.workspaces) {\n ux.stdout(\n styleText(\n 'gray',\n `↳ projectId: ${workspace.projectId}, dataset: ${workspace.dataset}, schemaDescriptorId: ${workspace.schemaDescriptorId}`,\n ),\n )\n }\n } else {\n ux.stdout(`${styleText('gray', '↳ No workspaces found')}`)\n }\n }\n\n return studioManifest\n } catch (error) {\n spin.fail(error instanceof Error ? error.message : 'Unknown error')\n throw error\n }\n}\n"],"names":["styleText","ux","doImport","getLocalPackageVersion","getProjectCliClient","resolveLocalPackage","subdebug","spinner","SCHEMA_API_VERSION","iconResolverPath","URL","url","href","debug","uploadSchemaToLexicon","options","projectId","verbose","workDir","workspaces","spin","start","schemaDescriptors","Map","client","apiVersion","requestTagPrefix","requireUser","bundleVersion","generateStudioManifest","uploadSchema","Promise","all","Error","workspace","workspaceClient","withConfig","dataset","descriptorId","schema","name","set","error","errorMessage","message","cause","resolveIcon","manifest","buildId","JSON","stringify","Date","now","icon","subtitle","title","undefined","resolveSchemaDescriptorId","get","succeed","studioManifest","length","stdout","schemaDescriptorId","fail"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,EAAE,QAAO,iBAAgB;AACjC,SACEC,QAAQ,EACRC,sBAAsB,EACtBC,mBAAmB,EACnBC,mBAAmB,EACnBC,QAAQ,QACH,mBAAkB;AACzB,SAAQC,OAAO,QAAO,sBAAqB;AAG3C,SAAQC,kBAAkB,QAAO,4BAA2B;AAE5D,MAAMC,mBAAmB,IAAIC,IAAI,+BAA+B,YAAYC,GAAG,EAAEC,IAAI;AAUrF,MAAMC,QAAQP,SAAS;AAEvB;;;;CAIC,GACD,OAAO,eAAeQ,sBACpBC,OAAqC;IAErC,MAAM,EAACC,SAAS,EAAEC,OAAO,EAAEC,OAAO,EAAEC,UAAU,EAAC,GAAGJ;IAClD,MAAMK,OAAOb,QAAQ,8BAA8Bc,KAAK;IAExD,IAAI;QACF,MAAMC,oBAAoB,IAAIC;QAE9B,MAAMC,SAAS,MAAMpB,oBAAoB;YACvCqB,YAAYjB;YACZQ;YACAU,kBAAkB;YAClBC,aAAa;QACf;QAEA,MAAM,CAACC,eAAe,EAACC,sBAAsB,EAAEC,YAAY,EAAC,CAAC,GAAG,MAAMC,QAAQC,GAAG,CAAC;YAChF7B,uBAAuB,UAAUe;YACjCb,oBAA6C,UAAUa;SACxD;QAED,IAAI,CAACU,eAAe;YAClB,MAAM,IAAIK,MAAM;QAClB;QAEA,KAAK,MAAMC,aAAaf,WAAY;YAClC,MAAMgB,kBAAkBX,OAAOY,UAAU,CAAC;gBACxCC,SAASH,UAAUG,OAAO;gBAC1BrB,WAAWkB,UAAUlB,SAAS;YAChC;YAEA,IAAI;gBACFH,MAAM,gDAAgD;oBACpDwB,SAASH,UAAUG,OAAO;oBAC1BrB,WAAWkB,UAAUlB,SAAS;gBAChC;gBACA,8DAA8D;gBAC9D,iEAAiE;gBACjE,2BAA2B;gBAC3B,MAAMsB,eAAe,MAAMR,aACzBI,UAAUK,MAAM,EAChBJ;gBAGF,IAAI,CAACG,cAAc;oBACjB,MAAM,IAAIL,MACR,CAAC,kDAAkD,EAAEC,UAAUM,IAAI,CAAC,+BAA+B,CAAC;gBAExG;gBAEAlB,kBAAkBmB,GAAG,CAACP,UAAUM,IAAI,EAAEF;gBACtCzB,MACE,CAAC,+BAA+B,EAAEqB,UAAUM,IAAI,CAAC,iCAAiC,EAAEF,cAAc;YAEtG,EAAE,OAAOI,OAAO;gBACd7B,MAAM,sDAAsD6B;gBAC5D,MAAMC,eAAeD,iBAAiBT,QAAQS,MAAME,OAAO,GAAG;gBAC9D,MAAM,IAAIX,MACR,CAAC,uCAAuC,EAAEC,UAAUM,IAAI,CAAC,GAAG,EAAEG,cAAc,EAC5E;oBAACE,OAAOH;gBAAK;YAEjB;QACF;QAEA,iEAAiE;QACjE,MAAM,EAACI,WAAW,EAAC,GAAG,MAAM5C,SAASO;QAErC,oDAAoD;QACpD,MAAMsC,WAAW,MAAMlB,uBAAuB;YAC5CmB,SAASC,KAAKC,SAAS,CAACC,KAAKC,GAAG;YAChCxB;YACA,oEAAoE;YACpEkB,aAAa,OAAOZ,YAClB,AAAC,MAAMY,YAAY;oBACjBO,MAAMnB,UAAUmB,IAAI;oBACpBC,UAAUpB,UAAUoB,QAAQ;oBAC5BC,OAAOrB,UAAUqB,KAAK,IAAIrB,UAAUM,IAAI,IAAI;oBAC5CtB;gBACF,MAAOsC;YACTC,2BAA2B,CAACvB,YAAcZ,kBAAkBoC,GAAG,CAACxB,UAAUM,IAAI;YAC9ErB;QACF;QAEAC,KAAKuC,OAAO,CAAC;QAEb,MAAMC,iBAAiBb,SAAS5B,UAAU,CAAC0C,MAAM,KAAK,IAAI,OAAOd;QAEjE,IAAI9B,SAAS;YACX,IAAI2C,gBAAgB;gBAClB,KAAK,MAAM1B,aAAa0B,eAAezC,UAAU,CAAE;oBACjDlB,GAAG6D,MAAM,CACP9D,UACE,QACA,CAAC,aAAa,EAAEkC,UAAUlB,SAAS,CAAC,WAAW,EAAEkB,UAAUG,OAAO,CAAC,sBAAsB,EAAEH,UAAU6B,kBAAkB,EAAE;gBAG/H;YACF,OAAO;gBACL9D,GAAG6D,MAAM,CAAC,GAAG9D,UAAU,QAAQ,0BAA0B;YAC3D;QACF;QAEA,OAAO4D;IACT,EAAE,OAAOlB,OAAO;QACdtB,KAAK4C,IAAI,CAACtB,iBAAiBT,QAAQS,MAAME,OAAO,GAAG;QACnD,MAAMF;IACR;AACF"}
@@ -1,4 +1,4 @@
1
- import debugIt from 'debug';
2
- export const telemetryDebug = debugIt('telemetry:sanity:cli');
1
+ import { createDebug } from 'obug';
2
+ export const telemetryDebug = createDebug('telemetry:sanity:cli');
3
3
 
4
4
  //# sourceMappingURL=telemetryDebug.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/telemetry/telemetryDebug.ts"],"sourcesContent":["import debugIt from 'debug'\n\nexport const telemetryDebug = debugIt('telemetry:sanity:cli')\n"],"names":["debugIt","telemetryDebug"],"mappings":"AAAA,OAAOA,aAAa,QAAO;AAE3B,OAAO,MAAMC,iBAAiBD,QAAQ,wBAAuB"}
1
+ {"version":3,"sources":["../../../src/actions/telemetry/telemetryDebug.ts"],"sourcesContent":["import {createDebug} from 'obug'\n\nexport const telemetryDebug = createDebug('telemetry:sanity:cli')\n"],"names":["createDebug","telemetryDebug"],"mappings":"AAAA,SAAQA,WAAW,QAAO,OAAM;AAEhC,OAAO,MAAMC,iBAAiBD,YAAY,wBAAuB"}
@@ -105,7 +105,7 @@ const undeployDebug = subdebug('undeploy');
105
105
  undeployDebug(`Error undeploying ${label}`, error);
106
106
  // Labeled here, where the target is known — `normalizeFailure` only sees the adapter type.
107
107
  throw new CLIError(`Error undeploying ${label}: ${getErrorMessage(error)}`, {
108
- exit: 1
108
+ exit: exitCodes.RUNTIME_ERROR
109
109
  });
110
110
  }
111
111
  spin.succeed();
@@ -188,7 +188,7 @@ function printUndeployed(target, output) {
188
188
  const label = type === 'coreApp' ? 'application' : 'studio';
189
189
  undeployDebug(`Error undeploying ${label}`, error);
190
190
  return {
191
- exit: 1,
191
+ exit: exitCodes.RUNTIME_ERROR,
192
192
  message: `Error undeploying ${label}: ${getErrorMessage(error)}`
193
193
  };
194
194
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/undeploy/runUndeploy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes, type Output, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {\n type UndeployAdapter,\n type UndeployTarget,\n type UndeployTargetResolution,\n} from '@sanity/cli-core/undeploy'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\n\nimport {type UndeployCommand} from '../../commands/undeploy.js'\nimport {\n type CheckReporter,\n createCollectingReporter,\n createFailFastReporter,\n} from '../../util/checks.js'\nimport {toStderrOutput} from '../../util/toStderrOutput.js'\nimport {\n describeUndeployTarget,\n renderUndeployPlan,\n undeployLabel,\n type UndeployPlan,\n undeployPlanToJson,\n} from './undeployPlan.js'\n\nconst undeployDebug = subdebug('undeploy')\n\ntype UndeployFlags = UndeployCommand['flags']\n\nexport interface UndeployOptions {\n flags: UndeployFlags\n isUnattended: boolean\n output: Output\n}\n\n/** What a real undeploy produced — the payload `--json` reports. */\ntype UndeployResult =\n | {application: null; payload: null; reason: string; undeployed: false}\n | {\n application: object | null\n deletes: UndeployTarget['deletes']\n payload: UndeployTarget['payload']\n reason: null\n undeployed: true\n url: string | null\n }\n\n/**\n * Runs an undeploy in the mode the flags select: a real undeploy fails fast,\n * confirms, and deletes; `--dry-run` drives the same target resolution\n * read-only and renders a plan instead. `--json` emits the same information as\n * machine-readable JSON.\n */\nexport async function runUndeploy(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<void> {\n const {flags, output} = options\n const json = !!flags.json\n const emitJson = (payload: unknown) => output.log(JSON.stringify(payload, null, 2))\n\n // The JSON payload owns stdout, so the run's progress logs go to stderr; only\n // the final JSON.stringify writes to stdout.\n const runOptions = json ? {...options, output: toStderrOutput(output)} : options\n\n try {\n if (flags['dry-run']) {\n const reporter = createCollectingReporter()\n const resolution = await resolveTarget(adapter, reporter)\n const plan: UndeployPlan = {\n checks: reporter.results,\n reason: resolution?.type === 'none' ? resolution.message : null,\n target: resolution?.type === 'found' ? resolution.target : null,\n type: adapter.type,\n }\n if (json) emitJson(undeployPlanToJson(plan))\n else renderUndeployPlan(plan, output)\n // A blocked plan exits like a real (fail-fast) undeploy would.\n const failed = plan.checks.find((check) => check.status === 'fail')\n if (failed) output.error('Undeploy blocked by failing checks.', {exit: failed.exitCode ?? 1})\n return\n }\n\n const result = await undeployApp(runOptions, adapter)\n if (json && result) emitJson(result)\n } catch (error) {\n const failure = normalizeFailure(error, adapter.type)\n // A blocked dry run reaches this catch too (its exit throws) and already\n // printed its plan, so only a real undeploy adds the {undeployed: false} envelope.\n if (json && !flags['dry-run']) {\n emitJson({\n application: null,\n error: {message: failure.message},\n payload: null,\n reason: failure.message,\n undeployed: false,\n })\n }\n output.error(failure.message, {exit: failure.exit})\n }\n}\n\n/** The real run: resolve the target, confirm, delete, report. */\nasync function undeployApp(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<UndeployResult | undefined> {\n const {flags, output} = options\n\n const resolution = await resolveTarget(adapter, createFailFastReporter(output))\n // A resolve failure never lands here: the fail-fast reporter already exited.\n if (!resolution) return undefined\n if (resolution.type === 'none') {\n output.log(`${resolution.message}.`)\n if (resolution.solution) output.log(`${resolution.solution}.`)\n output.log('Nothing to undeploy.')\n return {application: null, payload: null, reason: resolution.message, undeployed: false}\n }\n\n const {target} = resolution\n if (!flags.yes && options.isUnattended) {\n throw new CLIError('Undeploy requires confirmation. Pass --yes to continue.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (!flags.yes) {\n const shouldUndeploy = await confirm({\n default: false,\n message: confirmUndeployMessage(target),\n })\n if (!shouldUndeploy) {\n throw new CLIError('Undeploy cancelled', {exit: exitCodes.USER_ABORT})\n }\n }\n\n const label = undeployLabel(target, adapter.type)\n const spin = spinner(`Undeploying ${label}`).start()\n try {\n await adapter.undeploy(target)\n } catch (error) {\n spin.fail()\n undeployDebug(`Error undeploying ${label}`, error)\n // Labeled here, where the target is known — `normalizeFailure` only sees the adapter type.\n throw new CLIError(`Error undeploying ${label}: ${getErrorMessage(error)}`, {exit: 1})\n }\n spin.succeed()\n\n printUndeployed(target, output)\n return {\n application: target.application ?? null,\n deletes: target.deletes,\n payload: target.payload,\n reason: null,\n undeployed: true,\n url: target.url,\n }\n}\n\n/**\n * Resolves the undeploy target and reports the verdict as a check, shared by\n * both modes so a real run and a dry run can't diverge.\n */\nasync function resolveTarget(\n adapter: UndeployAdapter,\n reporter: CheckReporter,\n): Promise<UndeployTargetResolution | null> {\n const spin = spinner('Checking application info').start()\n\n let resolution: UndeployTargetResolution\n try {\n resolution = await adapter.resolveTarget()\n } catch (error) {\n spin.fail()\n undeployDebug('Failed to resolve undeploy target', error)\n reporter.report({\n message: `Failed to resolve undeploy target: ${getErrorMessage(error)}`,\n status: 'fail',\n })\n return null\n }\n\n spin[resolution.type === 'found' ? 'succeed' : 'fail']()\n reporter.report(describeUndeployTarget(resolution))\n return resolution\n}\n\nfunction confirmUndeployMessage(target: UndeployTarget): string {\n if (target.deletes === 'config') {\n return `This will delete the deployed config for ${styleText(\n 'yellow',\n target.title ?? 'this app',\n )}.\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n }\n\n if (target.type === 'coreApp') {\n return `This will undeploy the following application:\n\n Title: ${styleText('yellow', target.title || '(untitled application)')}\n ID: ${styleText('yellow', target.id)}\n\nThe application will no longer be available for any of your users if you proceed.\n\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n }\n\n return `This will undeploy ${styleText('yellow', target.url ?? target.id)} and make it unavailable for your users.\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n}\n\nfunction printUndeployed(target: UndeployTarget, output: Output): void {\n if (target.deletes === 'config') {\n output.log(`\\n${styleText('bold', 'Config deleted.')}`)\n return\n }\n\n if (target.type === 'coreApp') {\n output.log(\n `\\n${styleText('bold', '⏱️ Application undeploy scheduled.')} It might be a few minutes until ${\n target.title ? styleText('italic', `'${target.title}'`) : 'your application'\n } is unavailable.`,\n )\n output.log(\n `\\n${styleText('bold', 'Remember to remove `deployment.appId` from your application configuration')} to avoid errors when redeploying.`,\n )\n return\n }\n\n output.log(\n `\\nStudio undeploy scheduled. It might be a few minutes until ${target.url ?? 'your studio'} is unavailable.`,\n )\n}\n\n/** The one failure diagnosis both the stderr message and the `--json` envelope read. */\nfunction normalizeFailure(\n error: unknown,\n type: UndeployAdapter['type'],\n): {exit: number; message: string} {\n // Ctrl+C on the confirmation prompt isn't a real failure\n if (error instanceof Error && error.name === 'ExitPromptError') {\n return {exit: exitCodes.USER_ABORT, message: 'Undeploy cancelled'}\n }\n // A failed check already carries its own message and exit code\n if (error instanceof CLIError) {\n return {exit: error.oclif?.exit ?? 1, message: error.message}\n }\n const label = type === 'coreApp' ? 'application' : 'studio'\n undeployDebug(`Error undeploying ${label}`, error)\n return {exit: 1, message: `Error undeploying ${label}: ${getErrorMessage(error)}`}\n}\n"],"names":["styleText","CLIError","exitCodes","subdebug","getErrorMessage","confirm","spinner","createCollectingReporter","createFailFastReporter","toStderrOutput","describeUndeployTarget","renderUndeployPlan","undeployLabel","undeployPlanToJson","undeployDebug","runUndeploy","options","adapter","flags","output","json","emitJson","payload","log","JSON","stringify","runOptions","reporter","resolution","resolveTarget","plan","checks","results","reason","type","message","target","failed","find","check","status","error","exit","exitCode","result","undeployApp","failure","normalizeFailure","application","undeployed","undefined","solution","yes","isUnattended","USAGE_ERROR","shouldUndeploy","default","confirmUndeployMessage","USER_ABORT","label","spin","start","undeploy","fail","succeed","printUndeployed","deletes","url","report","title","id","Error","name","oclif"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/undeploy/runUndeploy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes, type Output, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {\n type UndeployAdapter,\n type UndeployTarget,\n type UndeployTargetResolution,\n} from '@sanity/cli-core/undeploy'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\n\nimport {type UndeployCommand} from '../../commands/undeploy.js'\nimport {\n type CheckReporter,\n createCollectingReporter,\n createFailFastReporter,\n} from '../../util/checks.js'\nimport {toStderrOutput} from '../../util/toStderrOutput.js'\nimport {\n describeUndeployTarget,\n renderUndeployPlan,\n undeployLabel,\n type UndeployPlan,\n undeployPlanToJson,\n} from './undeployPlan.js'\n\nconst undeployDebug = subdebug('undeploy')\n\ntype UndeployFlags = UndeployCommand['flags']\n\nexport interface UndeployOptions {\n flags: UndeployFlags\n isUnattended: boolean\n output: Output\n}\n\n/** What a real undeploy produced — the payload `--json` reports. */\ntype UndeployResult =\n | {application: null; payload: null; reason: string; undeployed: false}\n | {\n application: object | null\n deletes: UndeployTarget['deletes']\n payload: UndeployTarget['payload']\n reason: null\n undeployed: true\n url: string | null\n }\n\n/**\n * Runs an undeploy in the mode the flags select: a real undeploy fails fast,\n * confirms, and deletes; `--dry-run` drives the same target resolution\n * read-only and renders a plan instead. `--json` emits the same information as\n * machine-readable JSON.\n */\nexport async function runUndeploy(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<void> {\n const {flags, output} = options\n const json = !!flags.json\n const emitJson = (payload: unknown) => output.log(JSON.stringify(payload, null, 2))\n\n // The JSON payload owns stdout, so the run's progress logs go to stderr; only\n // the final JSON.stringify writes to stdout.\n const runOptions = json ? {...options, output: toStderrOutput(output)} : options\n\n try {\n if (flags['dry-run']) {\n const reporter = createCollectingReporter()\n const resolution = await resolveTarget(adapter, reporter)\n const plan: UndeployPlan = {\n checks: reporter.results,\n reason: resolution?.type === 'none' ? resolution.message : null,\n target: resolution?.type === 'found' ? resolution.target : null,\n type: adapter.type,\n }\n if (json) emitJson(undeployPlanToJson(plan))\n else renderUndeployPlan(plan, output)\n // A blocked plan exits like a real (fail-fast) undeploy would.\n const failed = plan.checks.find((check) => check.status === 'fail')\n if (failed) output.error('Undeploy blocked by failing checks.', {exit: failed.exitCode ?? 1})\n return\n }\n\n const result = await undeployApp(runOptions, adapter)\n if (json && result) emitJson(result)\n } catch (error) {\n const failure = normalizeFailure(error, adapter.type)\n // A blocked dry run reaches this catch too (its exit throws) and already\n // printed its plan, so only a real undeploy adds the {undeployed: false} envelope.\n if (json && !flags['dry-run']) {\n emitJson({\n application: null,\n error: {message: failure.message},\n payload: null,\n reason: failure.message,\n undeployed: false,\n })\n }\n output.error(failure.message, {exit: failure.exit})\n }\n}\n\n/** The real run: resolve the target, confirm, delete, report. */\nasync function undeployApp(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<UndeployResult | undefined> {\n const {flags, output} = options\n\n const resolution = await resolveTarget(adapter, createFailFastReporter(output))\n // A resolve failure never lands here: the fail-fast reporter already exited.\n if (!resolution) return undefined\n if (resolution.type === 'none') {\n output.log(`${resolution.message}.`)\n if (resolution.solution) output.log(`${resolution.solution}.`)\n output.log('Nothing to undeploy.')\n return {application: null, payload: null, reason: resolution.message, undeployed: false}\n }\n\n const {target} = resolution\n if (!flags.yes && options.isUnattended) {\n throw new CLIError('Undeploy requires confirmation. Pass --yes to continue.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (!flags.yes) {\n const shouldUndeploy = await confirm({\n default: false,\n message: confirmUndeployMessage(target),\n })\n if (!shouldUndeploy) {\n throw new CLIError('Undeploy cancelled', {exit: exitCodes.USER_ABORT})\n }\n }\n\n const label = undeployLabel(target, adapter.type)\n const spin = spinner(`Undeploying ${label}`).start()\n try {\n await adapter.undeploy(target)\n } catch (error) {\n spin.fail()\n undeployDebug(`Error undeploying ${label}`, error)\n // Labeled here, where the target is known — `normalizeFailure` only sees the adapter type.\n throw new CLIError(`Error undeploying ${label}: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n spin.succeed()\n\n printUndeployed(target, output)\n return {\n application: target.application ?? null,\n deletes: target.deletes,\n payload: target.payload,\n reason: null,\n undeployed: true,\n url: target.url,\n }\n}\n\n/**\n * Resolves the undeploy target and reports the verdict as a check, shared by\n * both modes so a real run and a dry run can't diverge.\n */\nasync function resolveTarget(\n adapter: UndeployAdapter,\n reporter: CheckReporter,\n): Promise<UndeployTargetResolution | null> {\n const spin = spinner('Checking application info').start()\n\n let resolution: UndeployTargetResolution\n try {\n resolution = await adapter.resolveTarget()\n } catch (error) {\n spin.fail()\n undeployDebug('Failed to resolve undeploy target', error)\n reporter.report({\n message: `Failed to resolve undeploy target: ${getErrorMessage(error)}`,\n status: 'fail',\n })\n return null\n }\n\n spin[resolution.type === 'found' ? 'succeed' : 'fail']()\n reporter.report(describeUndeployTarget(resolution))\n return resolution\n}\n\nfunction confirmUndeployMessage(target: UndeployTarget): string {\n if (target.deletes === 'config') {\n return `This will delete the deployed config for ${styleText(\n 'yellow',\n target.title ?? 'this app',\n )}.\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n }\n\n if (target.type === 'coreApp') {\n return `This will undeploy the following application:\n\n Title: ${styleText('yellow', target.title || '(untitled application)')}\n ID: ${styleText('yellow', target.id)}\n\nThe application will no longer be available for any of your users if you proceed.\n\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n }\n\n return `This will undeploy ${styleText('yellow', target.url ?? target.id)} and make it unavailable for your users.\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n}\n\nfunction printUndeployed(target: UndeployTarget, output: Output): void {\n if (target.deletes === 'config') {\n output.log(`\\n${styleText('bold', 'Config deleted.')}`)\n return\n }\n\n if (target.type === 'coreApp') {\n output.log(\n `\\n${styleText('bold', '⏱️ Application undeploy scheduled.')} It might be a few minutes until ${\n target.title ? styleText('italic', `'${target.title}'`) : 'your application'\n } is unavailable.`,\n )\n output.log(\n `\\n${styleText('bold', 'Remember to remove `deployment.appId` from your application configuration')} to avoid errors when redeploying.`,\n )\n return\n }\n\n output.log(\n `\\nStudio undeploy scheduled. It might be a few minutes until ${target.url ?? 'your studio'} is unavailable.`,\n )\n}\n\n/** The one failure diagnosis both the stderr message and the `--json` envelope read. */\nfunction normalizeFailure(\n error: unknown,\n type: UndeployAdapter['type'],\n): {exit: number; message: string} {\n // Ctrl+C on the confirmation prompt isn't a real failure\n if (error instanceof Error && error.name === 'ExitPromptError') {\n return {exit: exitCodes.USER_ABORT, message: 'Undeploy cancelled'}\n }\n // A failed check already carries its own message and exit code\n if (error instanceof CLIError) {\n return {exit: error.oclif?.exit ?? 1, message: error.message}\n }\n const label = type === 'coreApp' ? 'application' : 'studio'\n undeployDebug(`Error undeploying ${label}`, error)\n return {\n exit: exitCodes.RUNTIME_ERROR,\n message: `Error undeploying ${label}: ${getErrorMessage(error)}`,\n }\n}\n"],"names":["styleText","CLIError","exitCodes","subdebug","getErrorMessage","confirm","spinner","createCollectingReporter","createFailFastReporter","toStderrOutput","describeUndeployTarget","renderUndeployPlan","undeployLabel","undeployPlanToJson","undeployDebug","runUndeploy","options","adapter","flags","output","json","emitJson","payload","log","JSON","stringify","runOptions","reporter","resolution","resolveTarget","plan","checks","results","reason","type","message","target","failed","find","check","status","error","exit","exitCode","result","undeployApp","failure","normalizeFailure","application","undeployed","undefined","solution","yes","isUnattended","USAGE_ERROR","shouldUndeploy","default","confirmUndeployMessage","USER_ABORT","label","spin","start","undeploy","fail","RUNTIME_ERROR","succeed","printUndeployed","deletes","url","report","title","id","Error","name","oclif"],"mappings":"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"}
@@ -1,14 +1,11 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
3
  import { select } from '@sanity/cli-core/ux';
4
- import { isAfter } from 'date-fns/isAfter';
5
- import { isValid } from 'date-fns/isValid';
6
- import { lightFormat } from 'date-fns/lightFormat';
7
- import { parse } from 'date-fns/parse';
8
4
  import { assertDatasetExists } from '../../actions/backup/assertDatasetExist.js';
9
5
  import { promptForProject } from '../../prompts/promptForProject.js';
10
6
  import { listBackups } from '../../services/backup.js';
11
7
  import { listDatasets } from '../../services/datasets.js';
8
+ import { formatDateTime, parseDateOnly } from '../../util/dates.js';
12
9
  import { Table } from '../../util/responsiveTable.js';
13
10
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
14
11
  const listBackupDebug = subdebug('backup:list');
@@ -101,7 +98,7 @@ export class ListBackupCommand extends SanityCommand {
101
98
  try {
102
99
  const parsedBefore = this.processDateFlag(flags.before, 'before');
103
100
  const parsedAfter = this.processDateFlag(flags.after, 'after');
104
- if (parsedAfter && parsedBefore && isAfter(parsedAfter, parsedBefore)) {
101
+ if (parsedAfter && parsedBefore && parsedAfter.getTime() > parsedBefore.getTime()) {
105
102
  this.error('--after date must be before --before', {
106
103
  exit: exitCodes.RUNTIME_ERROR
107
104
  });
@@ -162,7 +159,7 @@ export class ListBackupCommand extends SanityCommand {
162
159
  const { createdAt, id } = backup;
163
160
  table.addRow({
164
161
  backupId: id,
165
- createdAt: lightFormat(Date.parse(createdAt), 'yyyy-MM-dd HH:mm:ss'),
162
+ createdAt: formatDateTime(createdAt),
166
163
  resource: 'Dataset'
167
164
  });
168
165
  }
@@ -178,8 +175,8 @@ export class ListBackupCommand extends SanityCommand {
178
175
  }
179
176
  processDateFlag(date, flagName) {
180
177
  if (!date) return undefined;
181
- const parsedDate = parse(date, 'yyyy-MM-dd', new Date());
182
- if (isValid(parsedDate)) {
178
+ const parsedDate = parseDateOnly(date);
179
+ if (parsedDate) {
183
180
  return parsedDate;
184
181
  }
185
182
  throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`);
@@ -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 {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 {Table} from '../../util/responsiveTable.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 this.log(table.render())\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","isAfter","isValid","lightFormat","parse","assertDatasetExists","promptForProject","listBackups","listDatasets","Table","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","render","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'\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 {formatDateTime, parseDateOnly} from '../../util/dates.js'\nimport {Table} from '../../util/responsiveTable.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 && parsedAfter.getTime() > parsedBefore.getTime()) {\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: formatDateTime(createdAt),\n resource: 'Dataset',\n })\n }\n\n this.log(table.render())\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 = parseDateOnly(date)\n if (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","assertDatasetExists","promptForProject","listBackups","listDatasets","formatDateTime","parseDateOnly","Table","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","getTime","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","resource","render","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
@@ -6,14 +6,12 @@ import { subdebug } from '@sanity/cli-core/debug';
6
6
  import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
7
7
  import { SanityCommand } from '@sanity/cli-core/SanityCommand';
8
8
  import { spinner } from '@sanity/cli-core/ux';
9
- import { formatDistance } from 'date-fns/formatDistance';
10
- import { formatDistanceToNow } from 'date-fns/formatDistanceToNow';
11
- import { parseISO } from 'date-fns/parseISO';
12
9
  import { validateDatasetName } from '../../actions/dataset/validateDatasetName.js';
13
10
  import { promptForDataset } from '../../prompts/promptForDataset.js';
14
11
  import { promptForDatasetName } from '../../prompts/promptForDatasetName.js';
15
12
  import { promptForProject } from '../../prompts/promptForProject.js';
16
13
  import { copyDataset, followCopyJobProgress, listDatasetCopyJobs, listDatasets } from '../../services/datasets.js';
14
+ import { formatDuration, formatTimeAgo } from '../../util/dates.js';
17
15
  import { formatCliErrorMessages } from '../../util/formatCliErrorMessages.js';
18
16
  import { Table } from '../../util/responsiveTable.js';
19
17
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
@@ -218,11 +216,11 @@ export class CopyDatasetCommand extends SanityCommand {
218
216
  const { createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory } = job;
219
217
  let timeStarted = '';
220
218
  if (createdAt !== '') {
221
- timeStarted = formatDistanceToNow(parseISO(createdAt));
219
+ timeStarted = formatTimeAgo(new Date(createdAt));
222
220
  }
223
221
  let timeTaken = '';
224
- if (updatedAt !== '') {
225
- timeTaken = formatDistance(parseISO(updatedAt), parseISO(createdAt));
222
+ if (createdAt !== '' && updatedAt !== '') {
223
+ timeTaken = formatDuration(new Date(updatedAt).getTime() - new Date(createdAt).getTime());
226
224
  }
227
225
  let color;
228
226
  switch(state){
@@ -251,7 +249,7 @@ export class CopyDatasetCommand extends SanityCommand {
251
249
  sourceDataset,
252
250
  state,
253
251
  targetDataset,
254
- timeStarted: `${timeStarted} ago`,
252
+ timeStarted,
255
253
  timeTaken,
256
254
  withHistory
257
255
  }, {