@sanity/cli 7.10.0 → 7.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +31 -22
  2. package/dist/actions/auth/ensureAuthenticated.js +2 -0
  3. package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
  4. package/dist/actions/auth/login/loginInstructions.js +19 -0
  5. package/dist/actions/auth/login/loginInstructions.js.map +1 -0
  6. package/dist/actions/build/buildApp.js +10 -7
  7. package/dist/actions/build/buildApp.js.map +1 -1
  8. package/dist/actions/build/buildStudio.js +9 -6
  9. package/dist/actions/build/buildStudio.js.map +1 -1
  10. package/dist/actions/build/types.js.map +1 -1
  11. package/dist/actions/dataset/resolveDataset.js +7 -1
  12. package/dist/actions/dataset/resolveDataset.js.map +1 -1
  13. package/dist/actions/deploy/deployApp.js +205 -157
  14. package/dist/actions/deploy/deployApp.js.map +1 -1
  15. package/dist/actions/deploy/deployChecks.js +1 -1
  16. package/dist/actions/deploy/deployChecks.js.map +1 -1
  17. package/dist/actions/deploy/deployStudio.js +127 -86
  18. package/dist/actions/deploy/deployStudio.js.map +1 -1
  19. package/dist/actions/deploy/resolveDeployTarget.js +5 -9
  20. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  21. package/dist/actions/dev/devAction.js +1 -2
  22. package/dist/actions/dev/devAction.js.map +1 -1
  23. package/dist/actions/dev/servers/getDevServerConfig.js +8 -2
  24. package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
  25. package/dist/actions/graphql/resolveApiGeneration.js +9 -6
  26. package/dist/actions/graphql/resolveApiGeneration.js.map +1 -1
  27. package/dist/actions/init/bootstrapLocalTemplate.js +18 -9
  28. package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
  29. package/dist/actions/init/createAppCliConfig.js.map +1 -1
  30. package/dist/actions/init/createCliConfig.js.map +1 -1
  31. package/dist/actions/init/initAction.js +23 -15
  32. package/dist/actions/init/initAction.js.map +1 -1
  33. package/dist/actions/manifest/extractCoreAppManifest.js +5 -3
  34. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  35. package/dist/actions/migration/constants.js +10 -0
  36. package/dist/actions/migration/constants.js.map +1 -0
  37. package/dist/actions/migration/ensureApiVersionFormat.js +16 -0
  38. package/dist/actions/migration/ensureApiVersionFormat.js.map +1 -0
  39. package/dist/actions/migration/fileExists.js +13 -0
  40. package/dist/actions/migration/fileExists.js.map +1 -0
  41. package/dist/actions/migration/prettyMutationFormatter.js +155 -0
  42. package/dist/actions/migration/prettyMutationFormatter.js.map +1 -0
  43. package/dist/actions/migration/resolveMigrationScript.js +75 -0
  44. package/dist/actions/migration/resolveMigrationScript.js.map +1 -0
  45. package/dist/actions/migration/resolveMigrations.js +50 -0
  46. package/dist/actions/migration/resolveMigrations.js.map +1 -0
  47. package/dist/actions/migration/templates/index.js +7 -0
  48. package/dist/actions/migration/templates/index.js.map +1 -0
  49. package/dist/actions/migration/templates/minimalAdvanced.js +21 -0
  50. package/dist/actions/migration/templates/minimalAdvanced.js.map +1 -0
  51. package/dist/actions/migration/templates/minimalSimple.js +61 -0
  52. package/dist/actions/migration/templates/minimalSimple.js.map +1 -0
  53. package/dist/actions/migration/templates/renameField.js +20 -0
  54. package/dist/actions/migration/templates/renameField.js.map +1 -0
  55. package/dist/actions/migration/templates/renameType.js +19 -0
  56. package/dist/actions/migration/templates/renameType.js.map +1 -0
  57. package/dist/actions/migration/templates/stringToPTE.js +32 -0
  58. package/dist/actions/migration/templates/stringToPTE.js.map +1 -0
  59. package/dist/actions/migration/tree.js +111 -0
  60. package/dist/actions/migration/tree.js.map +1 -0
  61. package/dist/actions/preview/previewAction.js +33 -1
  62. package/dist/actions/preview/previewAction.js.map +1 -1
  63. package/dist/actions/schema/listSchemas.js +6 -4
  64. package/dist/actions/schema/listSchemas.js.map +1 -1
  65. package/dist/actions/tokens/validateRole.js +2 -1
  66. package/dist/actions/tokens/validateRole.js.map +1 -1
  67. package/dist/actions/undeploy/runUndeploy.js +13 -4
  68. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  69. package/dist/commands/backups/disable.js +5 -1
  70. package/dist/commands/backups/disable.js.map +1 -1
  71. package/dist/commands/backups/download.js +30 -6
  72. package/dist/commands/backups/download.js.map +1 -1
  73. package/dist/commands/backups/enable.js +5 -1
  74. package/dist/commands/backups/enable.js.map +1 -1
  75. package/dist/commands/backups/list.js +5 -1
  76. package/dist/commands/backups/list.js.map +1 -1
  77. package/dist/commands/cors/add.js +15 -7
  78. package/dist/commands/cors/add.js.map +1 -1
  79. package/dist/commands/cors/delete.js +6 -1
  80. package/dist/commands/cors/delete.js.map +1 -1
  81. package/dist/commands/datasets/alias/create.js +25 -18
  82. package/dist/commands/datasets/alias/create.js.map +1 -1
  83. package/dist/commands/datasets/alias/delete.js +15 -8
  84. package/dist/commands/datasets/alias/delete.js.map +1 -1
  85. package/dist/commands/datasets/alias/link.js +39 -17
  86. package/dist/commands/datasets/alias/link.js.map +1 -1
  87. package/dist/commands/datasets/alias/unlink.js +23 -2
  88. package/dist/commands/datasets/alias/unlink.js.map +1 -1
  89. package/dist/commands/datasets/copy.js +18 -2
  90. package/dist/commands/datasets/copy.js.map +1 -1
  91. package/dist/commands/datasets/create.js +8 -2
  92. package/dist/commands/datasets/create.js.map +1 -1
  93. package/dist/commands/datasets/delete.js +14 -17
  94. package/dist/commands/datasets/delete.js.map +1 -1
  95. package/dist/commands/datasets/embeddings/disable.js +3 -0
  96. package/dist/commands/datasets/embeddings/disable.js.map +1 -1
  97. package/dist/commands/datasets/embeddings/enable.js +5 -2
  98. package/dist/commands/datasets/embeddings/enable.js.map +1 -1
  99. package/dist/commands/datasets/embeddings/status.js +3 -0
  100. package/dist/commands/datasets/embeddings/status.js.map +1 -1
  101. package/dist/commands/datasets/export.js +27 -15
  102. package/dist/commands/datasets/export.js.map +1 -1
  103. package/dist/commands/datasets/import.js +3 -3
  104. package/dist/commands/datasets/import.js.map +1 -1
  105. package/dist/commands/datasets/visibility/get.js +2 -2
  106. package/dist/commands/datasets/visibility/get.js.map +1 -1
  107. package/dist/commands/datasets/visibility/set.js +2 -2
  108. package/dist/commands/datasets/visibility/set.js.map +1 -1
  109. package/dist/commands/docs/read.js +3 -3
  110. package/dist/commands/docs/read.js.map +1 -1
  111. package/dist/commands/documents/create.js +18 -13
  112. package/dist/commands/documents/create.js.map +1 -1
  113. package/dist/commands/documents/delete.js +3 -3
  114. package/dist/commands/documents/delete.js.map +1 -1
  115. package/dist/commands/documents/get.js +3 -3
  116. package/dist/commands/documents/get.js.map +1 -1
  117. package/dist/commands/documents/query.js +3 -3
  118. package/dist/commands/documents/query.js.map +1 -1
  119. package/dist/commands/documents/validate.js +29 -26
  120. package/dist/commands/documents/validate.js.map +1 -1
  121. package/dist/commands/graphql/deploy.js +20 -16
  122. package/dist/commands/graphql/deploy.js.map +1 -1
  123. package/dist/commands/graphql/undeploy.js +15 -18
  124. package/dist/commands/graphql/undeploy.js.map +1 -1
  125. package/dist/commands/hooks/delete.js +2 -2
  126. package/dist/commands/hooks/delete.js.map +1 -1
  127. package/dist/commands/hooks/logs.js +2 -2
  128. package/dist/commands/hooks/logs.js.map +1 -1
  129. package/dist/commands/login.js +2 -1
  130. package/dist/commands/login.js.map +1 -1
  131. package/dist/commands/media/create-aspect.js +20 -8
  132. package/dist/commands/media/create-aspect.js.map +1 -1
  133. package/dist/commands/media/delete-aspect.js +31 -16
  134. package/dist/commands/media/delete-aspect.js.map +1 -1
  135. package/dist/commands/media/deploy-aspect.js +10 -5
  136. package/dist/commands/media/deploy-aspect.js.map +1 -1
  137. package/dist/commands/media/export.js +9 -4
  138. package/dist/commands/media/export.js.map +1 -1
  139. package/dist/commands/media/import.js +6 -1
  140. package/dist/commands/media/import.js.map +1 -1
  141. package/dist/commands/migrations/create.js +150 -0
  142. package/dist/commands/migrations/create.js.map +1 -0
  143. package/dist/commands/migrations/list.js +64 -0
  144. package/dist/commands/migrations/list.js.map +1 -0
  145. package/dist/commands/migrations/run.js +306 -0
  146. package/dist/commands/migrations/run.js.map +1 -0
  147. package/dist/commands/preview.js +1 -0
  148. package/dist/commands/preview.js.map +1 -1
  149. package/dist/commands/projects/create.js +2 -1
  150. package/dist/commands/projects/create.js.map +1 -1
  151. package/dist/commands/schemas/delete.js +23 -2
  152. package/dist/commands/schemas/delete.js.map +1 -1
  153. package/dist/commands/schemas/deploy.js +6 -4
  154. package/dist/commands/schemas/deploy.js.map +1 -1
  155. package/dist/commands/schemas/extract.js +22 -1
  156. package/dist/commands/schemas/extract.js.map +1 -1
  157. package/dist/commands/schemas/list.js +8 -8
  158. package/dist/commands/schemas/list.js.map +1 -1
  159. package/dist/commands/tokens/{add.js → create.js} +25 -18
  160. package/dist/commands/tokens/create.js.map +1 -0
  161. package/dist/commands/tokens/delete.js +41 -23
  162. package/dist/commands/tokens/delete.js.map +1 -1
  163. package/dist/commands/undeploy.js +2 -7
  164. package/dist/commands/undeploy.js.map +1 -1
  165. package/dist/commands/users/invite.js +25 -3
  166. package/dist/commands/users/invite.js.map +1 -1
  167. package/dist/topicAliases.js +3 -0
  168. package/dist/topicAliases.js.map +1 -1
  169. package/dist/util/formatCliErrorMessages.js +5 -0
  170. package/dist/util/formatCliErrorMessages.js.map +1 -0
  171. package/oclif.config.js +5 -1
  172. package/oclif.manifest.json +579 -374
  173. package/package.json +7 -7
  174. package/dist/commands/tokens/add.js.map +0 -1
@@ -0,0 +1,111 @@
1
+ import { isIndexSegment, isIndexTuple, isKeySegment } from '@sanity/types';
2
+ // copy/paste of `pathToString` from 'sanity' to prevent circular imports
3
+ function pathToString(path) {
4
+ if (!Array.isArray(path)) {
5
+ throw new TypeError('Path is not an array');
6
+ }
7
+ let target = '';
8
+ let i = 0;
9
+ for (const segment of path){
10
+ if (isIndexSegment(segment)) {
11
+ target = `${target}[${segment}]`;
12
+ } else if (isKeySegment(segment) && segment._key) {
13
+ target = `${target}[_key=="${segment._key}"]`;
14
+ } else if (isIndexTuple(segment)) {
15
+ const [from, to] = segment;
16
+ target = `${target}[${from}:${to}]`;
17
+ } else if (typeof segment === 'string') {
18
+ const separator = i === 0 ? '' : '.';
19
+ target = `${target}${separator}${segment}`;
20
+ } else {
21
+ throw new TypeError(`Unsupported path segment \`${JSON.stringify(segment)}\``);
22
+ }
23
+ i++;
24
+ }
25
+ return target;
26
+ }
27
+ /**
28
+ * Recursively calculates the max length of all the keys in the given validation
29
+ * tree respecting extra length due to indentation depth. Used to calculate the
30
+ * padding for the rest of the tree.
31
+ */ export const maxKeyLength = (children = {}, depth = 0)=>{
32
+ let max = 0;
33
+ for (const [key, child] of Object.entries(children)){
34
+ const currentMax = Math.max(key.length + depth * 2, maxKeyLength(child.children, depth + 1));
35
+ max = Math.max(max, currentMax);
36
+ }
37
+ return max;
38
+ };
39
+ /**
40
+ * Recursively formats a given tree into a printed user-friendly tree structure
41
+ */ export const formatTree = ({ getMessage, getNodes: getLeaves = ({ nodes })=>nodes, indent = '', node = {}, paddingLength })=>{
42
+ const entries = Object.entries(node);
43
+ return entries.map(([key, child], index)=>{
44
+ const isLast = index === entries.length - 1;
45
+ const nextIndent = `${indent}${isLast ? ' ' : '│ '}`;
46
+ const leaves = getLeaves(child);
47
+ const nested = formatTree({
48
+ getMessage,
49
+ getNodes: getLeaves,
50
+ indent: nextIndent,
51
+ node: child.children,
52
+ paddingLength
53
+ });
54
+ if (!leaves?.length) {
55
+ const current = `${indent}${isLast ? '└' : '├'}─ ${key}`;
56
+ return [
57
+ current,
58
+ nested
59
+ ].filter(Boolean).join('\n');
60
+ }
61
+ const first = leaves[0];
62
+ const rest = leaves.slice(1);
63
+ if (!first) return '';
64
+ const firstPadding = '.'.repeat(paddingLength - indent.length - key.length);
65
+ const elbow = isLast ? '└' : '├';
66
+ const subsequentPadding = ' '.repeat(paddingLength - indent.length + 2);
67
+ const firstMessage = `${indent}${elbow}─ ${key} ${firstPadding} ${getMessage(first)}`;
68
+ const subsequentMessages = rest.map((marker)=>`${nextIndent}${subsequentPadding} ${getMessage(marker)}`).join('\n');
69
+ const current = [
70
+ firstMessage,
71
+ subsequentMessages
72
+ ].filter(Boolean).join('\n');
73
+ return [
74
+ current,
75
+ nested
76
+ ].filter(Boolean).join('\n');
77
+ }).join('\n');
78
+ };
79
+ /**
80
+ * Converts a set of markers with paths into a tree of markers where the paths
81
+ * are embedded in the tree
82
+ */ export function convertToTree(nodes) {
83
+ const root = {};
84
+ // add the markers to the tree
85
+ function addNode(node, tree = root) {
86
+ // if we've traversed the whole path
87
+ if (node.path.length === 0) {
88
+ if (!tree.nodes) tree.nodes = []; // ensure markers is defined
89
+ // then add the marker to the front
90
+ tree.nodes.push(node);
91
+ return;
92
+ }
93
+ const current = node.path[0];
94
+ const rest = node.path.slice(1);
95
+ if (!current) return;
96
+ const key = pathToString([
97
+ current
98
+ ]);
99
+ // ensure the current node has children and the next node
100
+ if (!tree.children) tree.children = {};
101
+ if (!(key in tree.children)) tree.children[key] = {};
102
+ addNode({
103
+ ...node,
104
+ path: rest
105
+ }, tree.children[key]);
106
+ }
107
+ for (const node of nodes)addNode(node);
108
+ return root;
109
+ }
110
+
111
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/migration/tree.ts"],"sourcesContent":["import {isIndexSegment, isIndexTuple, isKeySegment, type Path} from '@sanity/types'\n\n// copy/paste of `pathToString` from 'sanity' to prevent circular imports\nfunction pathToString(path: Path): string {\n if (!Array.isArray(path)) {\n throw new TypeError('Path is not an array')\n }\n\n let target = ''\n let i = 0\n for (const segment of path) {\n if (isIndexSegment(segment)) {\n target = `${target}[${segment}]`\n } else if (isKeySegment(segment) && segment._key) {\n target = `${target}[_key==\"${segment._key}\"]`\n } else if (isIndexTuple(segment)) {\n const [from, to] = segment\n target = `${target}[${from}:${to}]`\n } else if (typeof segment === 'string') {\n const separator = i === 0 ? '' : '.'\n target = `${target}${separator}${segment}`\n } else {\n throw new TypeError(`Unsupported path segment \\`${JSON.stringify(segment)}\\``)\n }\n i++\n }\n return target\n}\n\ninterface BaseNode {\n path: Path\n}\n\ninterface Tree<Node extends BaseNode> {\n children?: Record<string, Tree<Node>>\n nodes?: Node[]\n}\n\n/**\n * Recursively calculates the max length of all the keys in the given validation\n * tree respecting extra length due to indentation depth. Used to calculate the\n * padding for the rest of the tree.\n */\nexport const maxKeyLength = (children: Record<string, Tree<BaseNode>> = {}, depth = 0): number => {\n let max = 0\n for (const [key, child] of Object.entries(children)) {\n const currentMax = Math.max(key.length + depth * 2, maxKeyLength(child.children, depth + 1))\n max = Math.max(max, currentMax)\n }\n return max\n}\n\ninterface Options<Node extends BaseNode> {\n getMessage: (node: Node) => string\n paddingLength: number\n\n getNodes?: (node: Tree<Node>) => Node[] | undefined\n indent?: string\n node?: Record<string, Tree<Node>>\n}\n\n/**\n * Recursively formats a given tree into a printed user-friendly tree structure\n */\nexport const formatTree = <Node extends BaseNode>({\n getMessage,\n getNodes: getLeaves = ({nodes}) => nodes,\n indent = '',\n node = {},\n paddingLength,\n}: Options<Node>): string => {\n const entries = Object.entries(node)\n\n return entries\n .map(([key, child], index) => {\n const isLast = index === entries.length - 1\n const nextIndent = `${indent}${isLast ? ' ' : '│ '}`\n const leaves = getLeaves(child)\n\n const nested = formatTree({\n getMessage,\n getNodes: getLeaves,\n indent: nextIndent,\n node: child.children,\n paddingLength,\n })\n\n if (!leaves?.length) {\n const current = `${indent}${isLast ? '└' : '├'}─ ${key}`\n return [current, nested].filter(Boolean).join('\\n')\n }\n\n const first = leaves[0]\n const rest = leaves.slice(1)\n if (!first) return ''\n const firstPadding = '.'.repeat(paddingLength - indent.length - key.length)\n const elbow = isLast ? '└' : '├'\n const subsequentPadding = ' '.repeat(paddingLength - indent.length + 2)\n\n const firstMessage = `${indent}${elbow}─ ${key} ${firstPadding} ${getMessage(first)}`\n const subsequentMessages = rest\n .map((marker) => `${nextIndent}${subsequentPadding} ${getMessage(marker)}`)\n .join('\\n')\n\n const current = [firstMessage, subsequentMessages].filter(Boolean).join('\\n')\n return [current, nested].filter(Boolean).join('\\n')\n })\n .join('\\n')\n}\n\n/**\n * Converts a set of markers with paths into a tree of markers where the paths\n * are embedded in the tree\n */\nexport function convertToTree<const Node extends BaseNode>(nodes: Node[]): Tree<Node> {\n const root: Tree<Node> = {}\n\n // add the markers to the tree\n function addNode(node: Node, tree: Tree<Node> = root) {\n // if we've traversed the whole path\n if (node.path.length === 0) {\n if (!tree.nodes) tree.nodes = [] // ensure markers is defined\n\n // then add the marker to the front\n tree.nodes.push(node)\n return\n }\n\n const current = node.path[0]\n const rest = node.path.slice(1)\n if (!current) return\n const key = pathToString([current])\n\n // ensure the current node has children and the next node\n if (!tree.children) tree.children = {}\n if (!(key in tree.children)) tree.children[key] = {}\n\n addNode({...node, path: rest}, tree.children[key])\n }\n\n for (const node of nodes) addNode(node)\n return root\n}\n"],"names":["isIndexSegment","isIndexTuple","isKeySegment","pathToString","path","Array","isArray","TypeError","target","i","segment","_key","from","to","separator","JSON","stringify","maxKeyLength","children","depth","max","key","child","Object","entries","currentMax","Math","length","formatTree","getMessage","getNodes","getLeaves","nodes","indent","node","paddingLength","map","index","isLast","nextIndent","leaves","nested","current","filter","Boolean","join","first","rest","slice","firstPadding","repeat","elbow","subsequentPadding","firstMessage","subsequentMessages","marker","convertToTree","root","addNode","tree","push"],"mappings":"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"}
@@ -1,14 +1,46 @@
1
+ import { SANITY_CACHE_DIR } from '@sanity/cli-build/_internal/build';
2
+ import { isWorkbenchApp } from '@sanity/workbench-cli';
1
3
  import { gracefulServerDeath } from '../../server/gracefulServerDeath.js';
2
4
  import { startPreviewServer } from '../../server/previewServer.js';
5
+ import { checkForDeprecatedAppId } from '../../util/appId.js';
6
+ import { determineIsApp } from '../../util/determineIsApp.js';
7
+ import { resolveReactStrictMode } from '../../util/resolveReactStrictMode.js';
8
+ import { extractCoreAppManifest } from '../manifest/extractCoreAppManifest.js';
9
+ import { extractStudioManifest } from '../manifest/extractStudioManifest.js';
3
10
  import { getPreviewServerConfig } from './getPreviewServerConfig.js';
4
11
  export async function previewAction(options) {
5
- const { cliConfig, flags, outDir, workDir } = options;
12
+ const { cliConfig, flags, outDir, output, workDir } = options;
6
13
  const config = getPreviewServerConfig({
7
14
  cliConfig,
8
15
  flags,
9
16
  rootDir: outDir,
10
17
  workDir
11
18
  });
19
+ if (isWorkbenchApp(cliConfig?.app)) {
20
+ const isApp = determineIsApp(cliConfig);
21
+ // Lazy so a non-workbench `sanity start` never loads the package. `doImport`
22
+ // is path-based and doesn't apply to a bare specifier.
23
+ // eslint-disable-next-line no-restricted-syntax
24
+ const { startWorkbenchPreview } = await import('@sanity/workbench-cli/preview');
25
+ return startWorkbenchPreview({
26
+ cacheDir: `${SANITY_CACHE_DIR}/vite`,
27
+ checkForDeprecatedAppId: ()=>checkForDeprecatedAppId({
28
+ cliConfig,
29
+ output
30
+ }),
31
+ cliConfig,
32
+ extractManifest: isApp ? ({ workDir: wd })=>extractCoreAppManifest({
33
+ workDir: wd
34
+ }) : (params)=>extractStudioManifest(params),
35
+ httpHost: config.httpHost,
36
+ httpPort: config.httpPort,
37
+ isApp,
38
+ outDir,
39
+ output,
40
+ reactStrictMode: resolveReactStrictMode(cliConfig) ?? false,
41
+ workDir
42
+ });
43
+ }
12
44
  try {
13
45
  const server = await startPreviewServer(config);
14
46
  return server;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/preview/previewAction.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\n\nimport {gracefulServerDeath} from '../../server/gracefulServerDeath.js'\nimport {startPreviewServer} from '../../server/previewServer.js'\nimport {getPreviewServerConfig} from './getPreviewServerConfig.js'\nimport {type PreviewFlags} from './types.js'\n\ninterface PreviewActionOptions {\n cliConfig: CliConfig\n flags: PreviewFlags\n outDir: string\n workDir: string\n}\n\nexport async function previewAction(options: PreviewActionOptions) {\n const {cliConfig, flags, outDir, workDir} = options\n\n const config = getPreviewServerConfig({cliConfig, flags, rootDir: outDir, workDir})\n\n try {\n const server = await startPreviewServer(config)\n return server\n } catch (err) {\n throw gracefulServerDeath('preview', config.httpHost, config.httpPort, err)\n }\n}\n"],"names":["gracefulServerDeath","startPreviewServer","getPreviewServerConfig","previewAction","options","cliConfig","flags","outDir","workDir","config","rootDir","server","err","httpHost","httpPort"],"mappings":"AAEA,SAAQA,mBAAmB,QAAO,sCAAqC;AACvE,SAAQC,kBAAkB,QAAO,gCAA+B;AAChE,SAAQC,sBAAsB,QAAO,8BAA6B;AAUlE,OAAO,eAAeC,cAAcC,OAA6B;IAC/D,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGJ;IAE5C,MAAMK,SAASP,uBAAuB;QAACG;QAAWC;QAAOI,SAASH;QAAQC;IAAO;IAEjF,IAAI;QACF,MAAMG,SAAS,MAAMV,mBAAmBQ;QACxC,OAAOE;IACT,EAAE,OAAOC,KAAK;QACZ,MAAMZ,oBAAoB,WAAWS,OAAOI,QAAQ,EAAEJ,OAAOK,QAAQ,EAAEF;IACzE;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/preview/previewAction.ts"],"sourcesContent":["import {SANITY_CACHE_DIR} from '@sanity/cli-build/_internal/build'\nimport {type CliConfig, type Output} from '@sanity/cli-core'\nimport {isWorkbenchApp} from '@sanity/workbench-cli'\n\nimport {gracefulServerDeath} from '../../server/gracefulServerDeath.js'\nimport {type PreviewServer, startPreviewServer} from '../../server/previewServer.js'\nimport {checkForDeprecatedAppId} from '../../util/appId.js'\nimport {determineIsApp} from '../../util/determineIsApp.js'\nimport {resolveReactStrictMode} from '../../util/resolveReactStrictMode.js'\nimport {extractCoreAppManifest} from '../manifest/extractCoreAppManifest.js'\nimport {extractStudioManifest} from '../manifest/extractStudioManifest.js'\nimport {getPreviewServerConfig} from './getPreviewServerConfig.js'\nimport {type PreviewFlags} from './types.js'\n\ninterface PreviewActionOptions {\n cliConfig: CliConfig\n flags: PreviewFlags\n outDir: string\n output: Output\n workDir: string\n}\n\nexport async function previewAction(\n options: PreviewActionOptions,\n): Promise<PreviewServer | {close: () => Promise<void>}> {\n const {cliConfig, flags, outDir, output, workDir} = options\n\n const config = getPreviewServerConfig({cliConfig, flags, rootDir: outDir, workDir})\n\n if (isWorkbenchApp(cliConfig?.app)) {\n const isApp = determineIsApp(cliConfig)\n // Lazy so a non-workbench `sanity start` never loads the package. `doImport`\n // is path-based and doesn't apply to a bare specifier.\n // eslint-disable-next-line no-restricted-syntax\n const {startWorkbenchPreview} = await import('@sanity/workbench-cli/preview')\n return startWorkbenchPreview({\n cacheDir: `${SANITY_CACHE_DIR}/vite`,\n checkForDeprecatedAppId: () => checkForDeprecatedAppId({cliConfig, output}),\n cliConfig,\n extractManifest: isApp\n ? ({workDir: wd}) => extractCoreAppManifest({workDir: wd})\n : (params) => extractStudioManifest(params),\n httpHost: config.httpHost,\n httpPort: config.httpPort,\n isApp,\n outDir,\n output,\n reactStrictMode: resolveReactStrictMode(cliConfig) ?? false,\n workDir,\n })\n }\n\n try {\n const server = await startPreviewServer(config)\n return server\n } catch (err) {\n throw gracefulServerDeath('preview', config.httpHost, config.httpPort, err)\n }\n}\n"],"names":["SANITY_CACHE_DIR","isWorkbenchApp","gracefulServerDeath","startPreviewServer","checkForDeprecatedAppId","determineIsApp","resolveReactStrictMode","extractCoreAppManifest","extractStudioManifest","getPreviewServerConfig","previewAction","options","cliConfig","flags","outDir","output","workDir","config","rootDir","app","isApp","startWorkbenchPreview","cacheDir","extractManifest","wd","params","httpHost","httpPort","reactStrictMode","server","err"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,oCAAmC;AAElE,SAAQC,cAAc,QAAO,wBAAuB;AAEpD,SAAQC,mBAAmB,QAAO,sCAAqC;AACvE,SAA4BC,kBAAkB,QAAO,gCAA+B;AACpF,SAAQC,uBAAuB,QAAO,sBAAqB;AAC3D,SAAQC,cAAc,QAAO,+BAA8B;AAC3D,SAAQC,sBAAsB,QAAO,uCAAsC;AAC3E,SAAQC,sBAAsB,QAAO,wCAAuC;AAC5E,SAAQC,qBAAqB,QAAO,uCAAsC;AAC1E,SAAQC,sBAAsB,QAAO,8BAA6B;AAWlE,OAAO,eAAeC,cACpBC,OAA6B;IAE7B,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGL;IAEpD,MAAMM,SAASR,uBAAuB;QAACG;QAAWC;QAAOK,SAASJ;QAAQE;IAAO;IAEjF,IAAIf,eAAeW,WAAWO,MAAM;QAClC,MAAMC,QAAQf,eAAeO;QAC7B,6EAA6E;QAC7E,uDAAuD;QACvD,gDAAgD;QAChD,MAAM,EAACS,qBAAqB,EAAC,GAAG,MAAM,MAAM,CAAC;QAC7C,OAAOA,sBAAsB;YAC3BC,UAAU,GAAGtB,iBAAiB,KAAK,CAAC;YACpCI,yBAAyB,IAAMA,wBAAwB;oBAACQ;oBAAWG;gBAAM;YACzEH;YACAW,iBAAiBH,QACb,CAAC,EAACJ,SAASQ,EAAE,EAAC,GAAKjB,uBAAuB;oBAACS,SAASQ;gBAAE,KACtD,CAACC,SAAWjB,sBAAsBiB;YACtCC,UAAUT,OAAOS,QAAQ;YACzBC,UAAUV,OAAOU,QAAQ;YACzBP;YACAN;YACAC;YACAa,iBAAiBtB,uBAAuBM,cAAc;YACtDI;QACF;IACF;IAEA,IAAI;QACF,MAAMa,SAAS,MAAM1B,mBAAmBc;QACxC,OAAOY;IACT,EAAE,OAAOC,KAAK;QACZ,MAAM5B,oBAAoB,WAAWe,OAAOS,QAAQ,EAAET,OAAOU,QAAQ,EAAEG;IACzE;AACF"}
@@ -26,7 +26,7 @@ export async function listSchemas(options) {
26
26
  }
27
27
  });
28
28
  const schemas = await getDatasetSchemas(projectDatasets, id);
29
- const parsedSchemas = parseSchemas(schemas, output);
29
+ const parsedSchemas = parseSchemas(schemas, output, json);
30
30
  if (parsedSchemas.length === 0) {
31
31
  const datasetString = getDatasetsOutString(projectDatasets.map((dataset)=>dataset.dataset));
32
32
  throw new Error(id ? `Schema for id "${id}" not found in ${datasetString}` : `No schemas found in ${datasetString}`);
@@ -56,16 +56,18 @@ async function getDatasetSchemas(projectDatasets, id) {
56
56
  }
57
57
  }));
58
58
  }
59
- function parseSchemas(schemas, output) {
59
+ function parseSchemas(schemas, output, json) {
60
60
  return schemas.map((schema)=>{
61
61
  if (schema.status === 'fulfilled') return schema.value;
62
62
  const error = schema.reason;
63
63
  if (error instanceof DatasetError) {
64
64
  if ('cause' in error && error.cause && typeof error.cause === 'object' && 'statusCode' in error.cause && error.cause.statusCode === 401) {
65
- output.log(styleText('yellow', `${logSymbols.warning} No permissions to read schema from "${error.dataset}". ${SCHEMA_PERMISSION_HELP_TEXT}:\n ${styleText('red', `${error.message}`)}`));
65
+ const logDiagnostic = json ? output.warn.bind(output) : output.log.bind(output);
66
+ logDiagnostic(styleText('yellow', `${logSymbols.warning} ↳ No permissions to read schema from "${error.dataset}". ${SCHEMA_PERMISSION_HELP_TEXT}:\n ${styleText('red', `${error.message}`)}`));
66
67
  return [];
67
68
  }
68
- output.log(styleText('red', `↳ Failed to fetch schema from "${error.dataset}":\n ${error.message}`));
69
+ const logDiagnostic = json ? output.warn.bind(output) : output.log.bind(output);
70
+ logDiagnostic(styleText('red', `↳ Failed to fetch schema from "${error.dataset}":\n ${error.message}`));
69
71
  return [];
70
72
  } else {
71
73
  //hubris inc: given the try-catch wrapping all the full promise "this should never happen"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/listSchemas.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type Output, studioWorkerTask} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\nimport {Table} from 'console-table-printer'\nimport {type Workspace} from 'sanity'\n\nimport {getSchemas} from '../../services/schemas.js'\nimport {isDefined} from '../manifest/schemaTypeHelpers.js'\nimport {type StoredWorkspaceSchema} from '../manifest/types.js'\nimport {type UniqWorkspaceWorkerData} from './types.js'\nimport {getDatasetsOutString} from './utils/schemaStoreOutStrings.js'\nimport {SCHEMA_PERMISSION_HELP_TEXT} from './utils/schemaStoreValidation.js'\n\ninterface ListSchemasOptions {\n configPath: string\n json: boolean\n output: Output\n workDir: string\n\n id?: string\n}\n\nclass DatasetError extends Error {\n public dataset: string\n public projectId: string\n constructor(args: {dataset: string; options?: ErrorOptions; projectId: string}) {\n super((args.options?.cause as {message?: string})?.message, args.options)\n this.projectId = args.projectId\n this.dataset = args.dataset\n this.name = 'DatasetError'\n }\n}\n\nexport async function listSchemas(options: ListSchemasOptions): Promise<void> {\n const {configPath, id, json, output, workDir} = options\n\n const projectDatasets = await studioWorkerTask<(Workspace & {key: string})[]>(\n new URL('uniqueWorkspaces.worker.js', import.meta.url),\n {\n name: 'uniqueWorkspaces',\n studioRootPath: workDir,\n workerData: {configPath} satisfies UniqWorkspaceWorkerData,\n },\n )\n\n const schemas = await getDatasetSchemas(projectDatasets, id)\n const parsedSchemas = parseSchemas(schemas, output)\n\n if (parsedSchemas.length === 0) {\n const datasetString = getDatasetsOutString(projectDatasets.map((dataset) => dataset.dataset))\n\n throw new Error(\n id\n ? `Schema for id \"${id}\" not found in ${datasetString}`\n : `No schemas found in ${datasetString}`,\n )\n }\n\n if (json) {\n output.log(JSON.stringify(id ? parsedSchemas[0] : parsedSchemas, null, 2))\n } else {\n printSchemas({output, schemas: parsedSchemas, workspaces: projectDatasets})\n }\n}\n\nasync function getDatasetSchemas(projectDatasets: Workspace[], id?: string) {\n return Promise.allSettled(\n projectDatasets.map(async ({dataset, projectId}) => {\n try {\n return await getSchemas(dataset, projectId, id)\n } catch (error) {\n throw new DatasetError({dataset, options: {cause: error}, projectId})\n }\n }),\n )\n}\n\nfunction parseSchemas(schemas: PromiseSettledResult<StoredWorkspaceSchema[]>[], output: Output) {\n return schemas\n .map((schema) => {\n if (schema.status === 'fulfilled') return schema.value\n\n const error = schema.reason\n\n if (error instanceof DatasetError) {\n if (\n 'cause' in error &&\n error.cause &&\n typeof error.cause === 'object' &&\n 'statusCode' in error.cause &&\n error.cause.statusCode === 401\n ) {\n output.log(\n styleText(\n 'yellow',\n `${logSymbols.warning} ↳ No permissions to read schema from \"${error.dataset}\". ${\n SCHEMA_PERMISSION_HELP_TEXT\n }:\\n ${styleText('red', `${error.message}`)}`,\n ),\n )\n return []\n }\n\n output.log(\n styleText('red', `↳ Failed to fetch schema from \"${error.dataset}\":\\n ${error.message}`),\n )\n return []\n } else {\n //hubris inc: given the try-catch wrapping all the full promise \"this should never happen\"\n throw error\n }\n })\n .filter((schema) => isDefined(schema))\n .flat()\n}\n\nfunction printSchemas({\n output,\n schemas,\n workspaces,\n}: {\n output: Output\n schemas: StoredWorkspaceSchema[]\n workspaces: Workspace[]\n}) {\n const rows = schemas\n .toSorted((a, b) => -(a._createdAt || '').localeCompare(b._createdAt || ''))\n .map(({_createdAt: createdAt, _id: id, workspace}) => {\n const workspaceData = workspaces.find((w) => w.name === workspace.name)\n if (!workspaceData) return\n\n return {\n CreatedAt: createdAt,\n Dataset: workspaceData.dataset,\n Id: id,\n ProjectId: workspaceData.projectId,\n Workspace: workspace.name,\n }\n })\n .filter((schema) => isDefined(schema))\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'Id'},\n {alignment: 'left', name: 'Workspace'},\n {alignment: 'left', name: 'Dataset'},\n {alignment: 'left', name: 'ProjectId'},\n {alignment: 'left', name: 'CreatedAt'},\n ],\n })\n\n table.addRows(rows)\n output.log(table.render())\n}\n"],"names":["styleText","studioWorkerTask","logSymbols","Table","getSchemas","isDefined","getDatasetsOutString","SCHEMA_PERMISSION_HELP_TEXT","DatasetError","Error","dataset","projectId","args","options","cause","message","name","listSchemas","configPath","id","json","output","workDir","projectDatasets","URL","url","studioRootPath","workerData","schemas","getDatasetSchemas","parsedSchemas","parseSchemas","length","datasetString","map","log","JSON","stringify","printSchemas","workspaces","Promise","allSettled","error","schema","status","value","reason","statusCode","warning","filter","flat","rows","toSorted","a","b","_createdAt","localeCompare","createdAt","_id","workspace","workspaceData","find","w","CreatedAt","Dataset","Id","ProjectId","Workspace","table","columns","alignment","addRows","render"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/schema/listSchemas.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type Output, studioWorkerTask} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\nimport {Table} from 'console-table-printer'\nimport {type Workspace} from 'sanity'\n\nimport {getSchemas} from '../../services/schemas.js'\nimport {isDefined} from '../manifest/schemaTypeHelpers.js'\nimport {type StoredWorkspaceSchema} from '../manifest/types.js'\nimport {type UniqWorkspaceWorkerData} from './types.js'\nimport {getDatasetsOutString} from './utils/schemaStoreOutStrings.js'\nimport {SCHEMA_PERMISSION_HELP_TEXT} from './utils/schemaStoreValidation.js'\n\ninterface ListSchemasOptions {\n configPath: string\n json: boolean\n output: Output\n workDir: string\n\n id?: string\n}\n\nclass DatasetError extends Error {\n public dataset: string\n public projectId: string\n constructor(args: {dataset: string; options?: ErrorOptions; projectId: string}) {\n super((args.options?.cause as {message?: string})?.message, args.options)\n this.projectId = args.projectId\n this.dataset = args.dataset\n this.name = 'DatasetError'\n }\n}\n\nexport async function listSchemas(options: ListSchemasOptions): Promise<void> {\n const {configPath, id, json, output, workDir} = options\n\n const projectDatasets = await studioWorkerTask<(Workspace & {key: string})[]>(\n new URL('uniqueWorkspaces.worker.js', import.meta.url),\n {\n name: 'uniqueWorkspaces',\n studioRootPath: workDir,\n workerData: {configPath} satisfies UniqWorkspaceWorkerData,\n },\n )\n\n const schemas = await getDatasetSchemas(projectDatasets, id)\n const parsedSchemas = parseSchemas(schemas, output, json)\n\n if (parsedSchemas.length === 0) {\n const datasetString = getDatasetsOutString(projectDatasets.map((dataset) => dataset.dataset))\n\n throw new Error(\n id\n ? `Schema for id \"${id}\" not found in ${datasetString}`\n : `No schemas found in ${datasetString}`,\n )\n }\n\n if (json) {\n output.log(JSON.stringify(id ? parsedSchemas[0] : parsedSchemas, null, 2))\n } else {\n printSchemas({output, schemas: parsedSchemas, workspaces: projectDatasets})\n }\n}\n\nasync function getDatasetSchemas(projectDatasets: Workspace[], id?: string) {\n return Promise.allSettled(\n projectDatasets.map(async ({dataset, projectId}) => {\n try {\n return await getSchemas(dataset, projectId, id)\n } catch (error) {\n throw new DatasetError({dataset, options: {cause: error}, projectId})\n }\n }),\n )\n}\n\nfunction parseSchemas(\n schemas: PromiseSettledResult<StoredWorkspaceSchema[]>[],\n output: Output,\n json: boolean,\n) {\n return schemas\n .map((schema) => {\n if (schema.status === 'fulfilled') return schema.value\n\n const error = schema.reason\n\n if (error instanceof DatasetError) {\n if (\n 'cause' in error &&\n error.cause &&\n typeof error.cause === 'object' &&\n 'statusCode' in error.cause &&\n error.cause.statusCode === 401\n ) {\n const logDiagnostic = json ? output.warn.bind(output) : output.log.bind(output)\n logDiagnostic(\n styleText(\n 'yellow',\n `${logSymbols.warning} ↳ No permissions to read schema from \"${error.dataset}\". ${\n SCHEMA_PERMISSION_HELP_TEXT\n }:\\n ${styleText('red', `${error.message}`)}`,\n ),\n )\n return []\n }\n\n const logDiagnostic = json ? output.warn.bind(output) : output.log.bind(output)\n logDiagnostic(\n styleText('red', `↳ Failed to fetch schema from \"${error.dataset}\":\\n ${error.message}`),\n )\n return []\n } else {\n //hubris inc: given the try-catch wrapping all the full promise \"this should never happen\"\n throw error\n }\n })\n .filter((schema) => isDefined(schema))\n .flat()\n}\n\nfunction printSchemas({\n output,\n schemas,\n workspaces,\n}: {\n output: Output\n schemas: StoredWorkspaceSchema[]\n workspaces: Workspace[]\n}) {\n const rows = schemas\n .toSorted((a, b) => -(a._createdAt || '').localeCompare(b._createdAt || ''))\n .map(({_createdAt: createdAt, _id: id, workspace}) => {\n const workspaceData = workspaces.find((w) => w.name === workspace.name)\n if (!workspaceData) return\n\n return {\n CreatedAt: createdAt,\n Dataset: workspaceData.dataset,\n Id: id,\n ProjectId: workspaceData.projectId,\n Workspace: workspace.name,\n }\n })\n .filter((schema) => isDefined(schema))\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'Id'},\n {alignment: 'left', name: 'Workspace'},\n {alignment: 'left', name: 'Dataset'},\n {alignment: 'left', name: 'ProjectId'},\n {alignment: 'left', name: 'CreatedAt'},\n ],\n })\n\n table.addRows(rows)\n output.log(table.render())\n}\n"],"names":["styleText","studioWorkerTask","logSymbols","Table","getSchemas","isDefined","getDatasetsOutString","SCHEMA_PERMISSION_HELP_TEXT","DatasetError","Error","dataset","projectId","args","options","cause","message","name","listSchemas","configPath","id","json","output","workDir","projectDatasets","URL","url","studioRootPath","workerData","schemas","getDatasetSchemas","parsedSchemas","parseSchemas","length","datasetString","map","log","JSON","stringify","printSchemas","workspaces","Promise","allSettled","error","schema","status","value","reason","statusCode","logDiagnostic","warn","bind","warning","filter","flat","rows","toSorted","a","b","_createdAt","localeCompare","createdAt","_id","workspace","workspaceData","find","w","CreatedAt","Dataset","Id","ProjectId","Workspace","table","columns","alignment","addRows","render"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { exitCodes } from '@sanity/cli-core';
1
2
  import { getTokenRoles } from '../../services/tokens.js';
2
3
  /**
3
4
  * Validate a role name
@@ -16,7 +17,7 @@ import { getTokenRoles } from '../../services/tokens.js';
16
17
  }
17
18
  const availableRoles = robotRoles.map((r)=>r.name).join(', ');
18
19
  return output.error(`Invalid role "${roleName}". Available roles: ${availableRoles}`, {
19
- exit: 1
20
+ exit: exitCodes.USAGE_ERROR
20
21
  });
21
22
  }
22
23
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/tokens/validateRole.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core'\n\nimport {getTokenRoles} from '../../services/tokens.js'\n\n/**\n * Validate a role name\n * @param roleName - The role name to validate\n * @param projectId - The project ID\n * @param output - Output to raise the error through the calling command\n * @returns A promise that resolves to the validated role name\n *\n * @internal\n */\nexport async function validateRole(\n roleName: string,\n projectId: string,\n output: Output,\n): Promise<string> {\n const roles = await getTokenRoles(projectId)\n const robotRoles = roles.filter((role) => role.appliesToRobots)\n\n const role = robotRoles.find((r) => r.name === roleName)\n if (role) {\n return roleName\n }\n\n const availableRoles = robotRoles.map((r) => r.name).join(', ')\n return output.error(`Invalid role \"${roleName}\". Available roles: ${availableRoles}`, {exit: 1})\n}\n"],"names":["getTokenRoles","validateRole","roleName","projectId","output","roles","robotRoles","filter","role","appliesToRobots","find","r","name","availableRoles","map","join","error","exit"],"mappings":"AAEA,SAAQA,aAAa,QAAO,2BAA0B;AAEtD;;;;;;;;CAQC,GACD,OAAO,eAAeC,aACpBC,QAAgB,EAChBC,SAAiB,EACjBC,MAAc;IAEd,MAAMC,QAAQ,MAAML,cAAcG;IAClC,MAAMG,aAAaD,MAAME,MAAM,CAAC,CAACC,OAASA,KAAKC,eAAe;IAE9D,MAAMD,OAAOF,WAAWI,IAAI,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKV;IAC/C,IAAIM,MAAM;QACR,OAAON;IACT;IAEA,MAAMW,iBAAiBP,WAAWQ,GAAG,CAAC,CAACH,IAAMA,EAAEC,IAAI,EAAEG,IAAI,CAAC;IAC1D,OAAOX,OAAOY,KAAK,CAAC,CAAC,cAAc,EAAEd,SAAS,oBAAoB,EAAEW,gBAAgB,EAAE;QAACI,MAAM;IAAC;AAChG"}
1
+ {"version":3,"sources":["../../../src/actions/tokens/validateRole.ts"],"sourcesContent":["import {exitCodes, type Output} from '@sanity/cli-core'\n\nimport {getTokenRoles} from '../../services/tokens.js'\n\n/**\n * Validate a role name\n * @param roleName - The role name to validate\n * @param projectId - The project ID\n * @param output - Output to raise the error through the calling command\n * @returns A promise that resolves to the validated role name\n *\n * @internal\n */\nexport async function validateRole(\n roleName: string,\n projectId: string,\n output: Output,\n): Promise<string> {\n const roles = await getTokenRoles(projectId)\n const robotRoles = roles.filter((role) => role.appliesToRobots)\n\n const role = robotRoles.find((r) => r.name === roleName)\n if (role) {\n return roleName\n }\n\n const availableRoles = robotRoles.map((r) => r.name).join(', ')\n return output.error(`Invalid role \"${roleName}\". Available roles: ${availableRoles}`, {\n exit: exitCodes.USAGE_ERROR,\n })\n}\n"],"names":["exitCodes","getTokenRoles","validateRole","roleName","projectId","output","roles","robotRoles","filter","role","appliesToRobots","find","r","name","availableRoles","map","join","error","exit","USAGE_ERROR"],"mappings":"AAAA,SAAQA,SAAS,QAAoB,mBAAkB;AAEvD,SAAQC,aAAa,QAAO,2BAA0B;AAEtD;;;;;;;;CAQC,GACD,OAAO,eAAeC,aACpBC,QAAgB,EAChBC,SAAiB,EACjBC,MAAc;IAEd,MAAMC,QAAQ,MAAML,cAAcG;IAClC,MAAMG,aAAaD,MAAME,MAAM,CAAC,CAACC,OAASA,KAAKC,eAAe;IAE9D,MAAMD,OAAOF,WAAWI,IAAI,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKV;IAC/C,IAAIM,MAAM;QACR,OAAON;IACT;IAEA,MAAMW,iBAAiBP,WAAWQ,GAAG,CAAC,CAACH,IAAMA,EAAEC,IAAI,EAAEG,IAAI,CAAC;IAC1D,OAAOX,OAAOY,KAAK,CAAC,CAAC,cAAc,EAAEd,SAAS,oBAAoB,EAAEW,gBAAgB,EAAE;QACpFI,MAAMlB,UAAUmB,WAAW;IAC7B;AACF"}
@@ -1,6 +1,6 @@
1
1
  import { styleText } from 'node:util';
2
2
  import { CLIError } from '@oclif/core/errors';
3
- import { subdebug } from '@sanity/cli-core';
3
+ import { exitCodes, subdebug } from '@sanity/cli-core';
4
4
  import { getErrorMessage } from '@sanity/cli-core/errors';
5
5
  import { confirm, spinner } from '@sanity/cli-core/ux';
6
6
  import { createCollectingReporter, createFailFastReporter } from '../../util/checks.js';
@@ -76,12 +76,21 @@ const undeployDebug = subdebug('undeploy');
76
76
  };
77
77
  }
78
78
  const { target } = resolution;
79
+ if (!flags.yes && options.isUnattended) {
80
+ throw new CLIError('Undeploy requires confirmation. Pass --yes to continue.', {
81
+ exit: exitCodes.USAGE_ERROR
82
+ });
83
+ }
79
84
  if (!flags.yes) {
80
85
  const shouldUndeploy = await confirm({
81
86
  default: false,
82
87
  message: confirmUndeployMessage(target)
83
88
  });
84
- if (!shouldUndeploy) return undefined;
89
+ if (!shouldUndeploy) {
90
+ throw new CLIError('Undeploy cancelled', {
91
+ exit: exitCodes.USER_ABORT
92
+ });
93
+ }
85
94
  }
86
95
  const label = undeployLabel(target, adapter.type);
87
96
  const spin = spinner(`Undeploying ${label}`).start();
@@ -157,8 +166,8 @@ function printUndeployed(target, output) {
157
166
  // Ctrl+C on the confirmation prompt isn't a real failure
158
167
  if (error instanceof Error && error.name === 'ExitPromptError') {
159
168
  return {
160
- exit: 1,
161
- message: 'Undeploy cancelled by user'
169
+ exit: exitCodes.USER_ABORT,
170
+ message: 'Undeploy cancelled'
162
171
  };
163
172
  }
164
173
  // A failed check already carries its own message and exit code
@@ -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 {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 output: Output\n}\n\n/** What a real undeploy produced — the payload `--json` reports. */\ntype UndeployResult =\n | {application: UndeployTarget; undeployed: true}\n | {reason: string; undeployed: false}\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 // The report-only `summary` lines never enter the payload\n const emitJson = (payload: unknown) =>\n output.log(JSON.stringify(payload, (key, value) => (key === 'summary' ? undefined : value), 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({error: {message: failure.message}, undeployed: false})\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 {reason: resolution.message, undeployed: false}\n }\n\n const {target} = resolution\n if (!flags.yes) {\n const shouldUndeploy = await confirm({\n default: false,\n message: confirmUndeployMessage(target),\n })\n if (!shouldUndeploy) return undefined\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 {application: target, undeployed: true}\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: 1, message: 'Undeploy cancelled by user'}\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","subdebug","getErrorMessage","confirm","spinner","createCollectingReporter","createFailFastReporter","toStderrOutput","describeUndeployTarget","renderUndeployPlan","undeployLabel","undeployPlanToJson","undeployDebug","runUndeploy","options","adapter","flags","output","json","emitJson","payload","log","JSON","stringify","key","value","undefined","runOptions","reporter","resolution","resolveTarget","plan","checks","results","reason","type","message","target","failed","find","check","status","error","exit","exitCode","result","undeployApp","failure","normalizeFailure","undeployed","solution","yes","shouldUndeploy","default","confirmUndeployMessage","label","spin","start","undeploy","fail","succeed","printUndeployed","application","report","deletes","title","id","url","Error","name","oclif"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,QAAQ,QAAO,qBAAoB;AAC3C,SAAqBC,QAAQ,QAAO,mBAAkB;AACtD,SAAQC,eAAe,QAAO,0BAAyB;AAMvD,SAAQC,OAAO,EAAEC,OAAO,QAAO,sBAAqB;AAGpD,SAEEC,wBAAwB,EACxBC,sBAAsB,QACjB,uBAAsB;AAC7B,SAAQC,cAAc,QAAO,+BAA8B;AAC3D,SACEC,sBAAsB,EACtBC,kBAAkB,EAClBC,aAAa,EAEbC,kBAAkB,QACb,oBAAmB;AAE1B,MAAMC,gBAAgBX,SAAS;AAc/B;;;;;CAKC,GACD,OAAO,eAAeY,YACpBC,OAAwB,EACxBC,OAAwB;IAExB,MAAM,EAACC,KAAK,EAAEC,MAAM,EAAC,GAAGH;IACxB,MAAMI,OAAO,CAAC,CAACF,MAAME,IAAI;IACzB,0DAA0D;IAC1D,MAAMC,WAAW,CAACC,UAChBH,OAAOI,GAAG,CAACC,KAAKC,SAAS,CAACH,SAAS,CAACI,KAAKC,QAAWD,QAAQ,YAAYE,YAAYD,OAAQ;IAE9F,8EAA8E;IAC9E,6CAA6C;IAC7C,MAAME,aAAaT,OAAO;QAAC,GAAGJ,OAAO;QAAEG,QAAQV,eAAeU;IAAO,IAAIH;IAEzE,IAAI;QACF,IAAIE,KAAK,CAAC,UAAU,EAAE;YACpB,MAAMY,WAAWvB;YACjB,MAAMwB,aAAa,MAAMC,cAAcf,SAASa;YAChD,MAAMG,OAAqB;gBACzBC,QAAQJ,SAASK,OAAO;gBACxBC,QAAQL,YAAYM,SAAS,SAASN,WAAWO,OAAO,GAAG;gBAC3DC,QAAQR,YAAYM,SAAS,UAAUN,WAAWQ,MAAM,GAAG;gBAC3DF,MAAMpB,QAAQoB,IAAI;YACpB;YACA,IAAIjB,MAAMC,SAASR,mBAAmBoB;iBACjCtB,mBAAmBsB,MAAMd;YAC9B,+DAA+D;YAC/D,MAAMqB,SAASP,KAAKC,MAAM,CAACO,IAAI,CAAC,CAACC,QAAUA,MAAMC,MAAM,KAAK;YAC5D,IAAIH,QAAQrB,OAAOyB,KAAK,CAAC,uCAAuC;gBAACC,MAAML,OAAOM,QAAQ,IAAI;YAAC;YAC3F;QACF;QAEA,MAAMC,SAAS,MAAMC,YAAYnB,YAAYZ;QAC7C,IAAIG,QAAQ2B,QAAQ1B,SAAS0B;IAC/B,EAAE,OAAOH,OAAO;QACd,MAAMK,UAAUC,iBAAiBN,OAAO3B,QAAQoB,IAAI;QACpD,yEAAyE;QACzE,mFAAmF;QACnF,IAAIjB,QAAQ,CAACF,KAAK,CAAC,UAAU,EAAE;YAC7BG,SAAS;gBAACuB,OAAO;oBAACN,SAASW,QAAQX,OAAO;gBAAA;gBAAGa,YAAY;YAAK;QAChE;QACAhC,OAAOyB,KAAK,CAACK,QAAQX,OAAO,EAAE;YAACO,MAAMI,QAAQJ,IAAI;QAAA;IACnD;AACF;AAEA,+DAA+D,GAC/D,eAAeG,YACbhC,OAAwB,EACxBC,OAAwB;IAExB,MAAM,EAACC,KAAK,EAAEC,MAAM,EAAC,GAAGH;IAExB,MAAMe,aAAa,MAAMC,cAAcf,SAAST,uBAAuBW;IACvE,6EAA6E;IAC7E,IAAI,CAACY,YAAY,OAAOH;IACxB,IAAIG,WAAWM,IAAI,KAAK,QAAQ;QAC9BlB,OAAOI,GAAG,CAAC,GAAGQ,WAAWO,OAAO,CAAC,CAAC,CAAC;QACnC,IAAIP,WAAWqB,QAAQ,EAAEjC,OAAOI,GAAG,CAAC,GAAGQ,WAAWqB,QAAQ,CAAC,CAAC,CAAC;QAC7DjC,OAAOI,GAAG,CAAC;QACX,OAAO;YAACa,QAAQL,WAAWO,OAAO;YAAEa,YAAY;QAAK;IACvD;IAEA,MAAM,EAACZ,MAAM,EAAC,GAAGR;IACjB,IAAI,CAACb,MAAMmC,GAAG,EAAE;QACd,MAAMC,iBAAiB,MAAMjD,QAAQ;YACnCkD,SAAS;YACTjB,SAASkB,uBAAuBjB;QAClC;QACA,IAAI,CAACe,gBAAgB,OAAO1B;IAC9B;IAEA,MAAM6B,QAAQ7C,cAAc2B,QAAQtB,QAAQoB,IAAI;IAChD,MAAMqB,OAAOpD,QAAQ,CAAC,YAAY,EAAEmD,OAAO,EAAEE,KAAK;IAClD,IAAI;QACF,MAAM1C,QAAQ2C,QAAQ,CAACrB;IACzB,EAAE,OAAOK,OAAO;QACdc,KAAKG,IAAI;QACT/C,cAAc,CAAC,kBAAkB,EAAE2C,OAAO,EAAEb;QAC5C,2FAA2F;QAC3F,MAAM,IAAI1C,SAAS,CAAC,kBAAkB,EAAEuD,MAAM,EAAE,EAAErD,gBAAgBwC,QAAQ,EAAE;YAACC,MAAM;QAAC;IACtF;IACAa,KAAKI,OAAO;IAEZC,gBAAgBxB,QAAQpB;IACxB,OAAO;QAAC6C,aAAazB;QAAQY,YAAY;IAAI;AAC/C;AAEA;;;CAGC,GACD,eAAenB,cACbf,OAAwB,EACxBa,QAAuB;IAEvB,MAAM4B,OAAOpD,QAAQ,6BAA6BqD,KAAK;IAEvD,IAAI5B;IACJ,IAAI;QACFA,aAAa,MAAMd,QAAQe,aAAa;IAC1C,EAAE,OAAOY,OAAO;QACdc,KAAKG,IAAI;QACT/C,cAAc,qCAAqC8B;QACnDd,SAASmC,MAAM,CAAC;YACd3B,SAAS,CAAC,mCAAmC,EAAElC,gBAAgBwC,QAAQ;YACvED,QAAQ;QACV;QACA,OAAO;IACT;IAEAe,IAAI,CAAC3B,WAAWM,IAAI,KAAK,UAAU,YAAY,OAAO;IACtDP,SAASmC,MAAM,CAACvD,uBAAuBqB;IACvC,OAAOA;AACT;AAEA,SAASyB,uBAAuBjB,MAAsB;IACpD,IAAIA,OAAO2B,OAAO,KAAK,UAAU;QAC/B,OAAO,CAAC,yCAAyC,EAAEjE,UACjD,UACAsC,OAAO4B,KAAK,IAAI,YAChB;QACE,EAAElE,UAAU,OAAO,QAAQ,sBAAsB,CAAC;IACxD;IAEA,IAAIsC,OAAOF,IAAI,KAAK,WAAW;QAC7B,OAAO,CAAC;;WAED,EAAEpC,UAAU,UAAUsC,OAAO4B,KAAK,IAAI,0BAA0B;WAChE,EAAElE,UAAU,UAAUsC,OAAO6B,EAAE,EAAE;;;;QAIpC,EAAEnE,UAAU,OAAO,QAAQ,sBAAsB,CAAC;IACxD;IAEA,OAAO,CAAC,mBAAmB,EAAEA,UAAU,UAAUsC,OAAO8B,GAAG,IAAI9B,OAAO6B,EAAE,EAAE;QACpE,EAAEnE,UAAU,OAAO,QAAQ,sBAAsB,CAAC;AAC1D;AAEA,SAAS8D,gBAAgBxB,MAAsB,EAAEpB,MAAc;IAC7D,IAAIoB,OAAO2B,OAAO,KAAK,UAAU;QAC/B/C,OAAOI,GAAG,CAAC,CAAC,EAAE,EAAEtB,UAAU,QAAQ,oBAAoB;QACtD;IACF;IAEA,IAAIsC,OAAOF,IAAI,KAAK,WAAW;QAC7BlB,OAAOI,GAAG,CACR,CAAC,EAAE,EAAEtB,UAAU,QAAQ,sCAAsC,iCAAiC,EAC5FsC,OAAO4B,KAAK,GAAGlE,UAAU,UAAU,CAAC,CAAC,EAAEsC,OAAO4B,KAAK,CAAC,CAAC,CAAC,IAAI,mBAC3D,gBAAgB,CAAC;QAEpBhD,OAAOI,GAAG,CACR,CAAC,EAAE,EAAEtB,UAAU,QAAQ,6EAA6E,kCAAkC,CAAC;QAEzI;IACF;IAEAkB,OAAOI,GAAG,CACR,CAAC,6DAA6D,EAAEgB,OAAO8B,GAAG,IAAI,cAAc,gBAAgB,CAAC;AAEjH;AAEA,sFAAsF,GACtF,SAASnB,iBACPN,KAAc,EACdP,IAA6B;IAE7B,yDAAyD;IACzD,IAAIO,iBAAiB0B,SAAS1B,MAAM2B,IAAI,KAAK,mBAAmB;QAC9D,OAAO;YAAC1B,MAAM;YAAGP,SAAS;QAA4B;IACxD;IACA,+DAA+D;IAC/D,IAAIM,iBAAiB1C,UAAU;QAC7B,OAAO;YAAC2C,MAAMD,MAAM4B,KAAK,EAAE3B,QAAQ;YAAGP,SAASM,MAAMN,OAAO;QAAA;IAC9D;IACA,MAAMmB,QAAQpB,SAAS,YAAY,gBAAgB;IACnDvB,cAAc,CAAC,kBAAkB,EAAE2C,OAAO,EAAEb;IAC5C,OAAO;QAACC,MAAM;QAAGP,SAAS,CAAC,kBAAkB,EAAEmB,MAAM,EAAE,EAAErD,gBAAgBwC,QAAQ;IAAA;AACnF"}
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: UndeployTarget; undeployed: true}\n | {reason: string; undeployed: false}\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 // The report-only `summary` lines never enter the payload\n const emitJson = (payload: unknown) =>\n output.log(JSON.stringify(payload, (key, value) => (key === 'summary' ? undefined : value), 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({error: {message: failure.message}, undeployed: false})\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 {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 {application: target, undeployed: true}\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","key","value","undefined","runOptions","reporter","resolution","resolveTarget","plan","checks","results","reason","type","message","target","failed","find","check","status","error","exit","exitCode","result","undeployApp","failure","normalizeFailure","undeployed","solution","yes","isUnattended","USAGE_ERROR","shouldUndeploy","default","confirmUndeployMessage","USER_ABORT","label","spin","start","undeploy","fail","succeed","printUndeployed","application","report","deletes","title","id","url","Error","name","oclif"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,SAAS,EAAeC,QAAQ,QAAO,mBAAkB;AACjE,SAAQC,eAAe,QAAO,0BAAyB;AAMvD,SAAQC,OAAO,EAAEC,OAAO,QAAO,sBAAqB;AAGpD,SAEEC,wBAAwB,EACxBC,sBAAsB,QACjB,uBAAsB;AAC7B,SAAQC,cAAc,QAAO,+BAA8B;AAC3D,SACEC,sBAAsB,EACtBC,kBAAkB,EAClBC,aAAa,EAEbC,kBAAkB,QACb,oBAAmB;AAE1B,MAAMC,gBAAgBX,SAAS;AAe/B;;;;;CAKC,GACD,OAAO,eAAeY,YACpBC,OAAwB,EACxBC,OAAwB;IAExB,MAAM,EAACC,KAAK,EAAEC,MAAM,EAAC,GAAGH;IACxB,MAAMI,OAAO,CAAC,CAACF,MAAME,IAAI;IACzB,0DAA0D;IAC1D,MAAMC,WAAW,CAACC,UAChBH,OAAOI,GAAG,CAACC,KAAKC,SAAS,CAACH,SAAS,CAACI,KAAKC,QAAWD,QAAQ,YAAYE,YAAYD,OAAQ;IAE9F,8EAA8E;IAC9E,6CAA6C;IAC7C,MAAME,aAAaT,OAAO;QAAC,GAAGJ,OAAO;QAAEG,QAAQV,eAAeU;IAAO,IAAIH;IAEzE,IAAI;QACF,IAAIE,KAAK,CAAC,UAAU,EAAE;YACpB,MAAMY,WAAWvB;YACjB,MAAMwB,aAAa,MAAMC,cAAcf,SAASa;YAChD,MAAMG,OAAqB;gBACzBC,QAAQJ,SAASK,OAAO;gBACxBC,QAAQL,YAAYM,SAAS,SAASN,WAAWO,OAAO,GAAG;gBAC3DC,QAAQR,YAAYM,SAAS,UAAUN,WAAWQ,MAAM,GAAG;gBAC3DF,MAAMpB,QAAQoB,IAAI;YACpB;YACA,IAAIjB,MAAMC,SAASR,mBAAmBoB;iBACjCtB,mBAAmBsB,MAAMd;YAC9B,+DAA+D;YAC/D,MAAMqB,SAASP,KAAKC,MAAM,CAACO,IAAI,CAAC,CAACC,QAAUA,MAAMC,MAAM,KAAK;YAC5D,IAAIH,QAAQrB,OAAOyB,KAAK,CAAC,uCAAuC;gBAACC,MAAML,OAAOM,QAAQ,IAAI;YAAC;YAC3F;QACF;QAEA,MAAMC,SAAS,MAAMC,YAAYnB,YAAYZ;QAC7C,IAAIG,QAAQ2B,QAAQ1B,SAAS0B;IAC/B,EAAE,OAAOH,OAAO;QACd,MAAMK,UAAUC,iBAAiBN,OAAO3B,QAAQoB,IAAI;QACpD,yEAAyE;QACzE,mFAAmF;QACnF,IAAIjB,QAAQ,CAACF,KAAK,CAAC,UAAU,EAAE;YAC7BG,SAAS;gBAACuB,OAAO;oBAACN,SAASW,QAAQX,OAAO;gBAAA;gBAAGa,YAAY;YAAK;QAChE;QACAhC,OAAOyB,KAAK,CAACK,QAAQX,OAAO,EAAE;YAACO,MAAMI,QAAQJ,IAAI;QAAA;IACnD;AACF;AAEA,+DAA+D,GAC/D,eAAeG,YACbhC,OAAwB,EACxBC,OAAwB;IAExB,MAAM,EAACC,KAAK,EAAEC,MAAM,EAAC,GAAGH;IAExB,MAAMe,aAAa,MAAMC,cAAcf,SAAST,uBAAuBW;IACvE,6EAA6E;IAC7E,IAAI,CAACY,YAAY,OAAOH;IACxB,IAAIG,WAAWM,IAAI,KAAK,QAAQ;QAC9BlB,OAAOI,GAAG,CAAC,GAAGQ,WAAWO,OAAO,CAAC,CAAC,CAAC;QACnC,IAAIP,WAAWqB,QAAQ,EAAEjC,OAAOI,GAAG,CAAC,GAAGQ,WAAWqB,QAAQ,CAAC,CAAC,CAAC;QAC7DjC,OAAOI,GAAG,CAAC;QACX,OAAO;YAACa,QAAQL,WAAWO,OAAO;YAAEa,YAAY;QAAK;IACvD;IAEA,MAAM,EAACZ,MAAM,EAAC,GAAGR;IACjB,IAAI,CAACb,MAAMmC,GAAG,IAAIrC,QAAQsC,YAAY,EAAE;QACtC,MAAM,IAAIrD,SAAS,2DAA2D;YAC5E4C,MAAM3C,UAAUqD,WAAW;QAC7B;IACF;IAEA,IAAI,CAACrC,MAAMmC,GAAG,EAAE;QACd,MAAMG,iBAAiB,MAAMnD,QAAQ;YACnCoD,SAAS;YACTnB,SAASoB,uBAAuBnB;QAClC;QACA,IAAI,CAACiB,gBAAgB;YACnB,MAAM,IAAIvD,SAAS,sBAAsB;gBAAC4C,MAAM3C,UAAUyD,UAAU;YAAA;QACtE;IACF;IAEA,MAAMC,QAAQhD,cAAc2B,QAAQtB,QAAQoB,IAAI;IAChD,MAAMwB,OAAOvD,QAAQ,CAAC,YAAY,EAAEsD,OAAO,EAAEE,KAAK;IAClD,IAAI;QACF,MAAM7C,QAAQ8C,QAAQ,CAACxB;IACzB,EAAE,OAAOK,OAAO;QACdiB,KAAKG,IAAI;QACTlD,cAAc,CAAC,kBAAkB,EAAE8C,OAAO,EAAEhB;QAC5C,2FAA2F;QAC3F,MAAM,IAAI3C,SAAS,CAAC,kBAAkB,EAAE2D,MAAM,EAAE,EAAExD,gBAAgBwC,QAAQ,EAAE;YAACC,MAAM;QAAC;IACtF;IACAgB,KAAKI,OAAO;IAEZC,gBAAgB3B,QAAQpB;IACxB,OAAO;QAACgD,aAAa5B;QAAQY,YAAY;IAAI;AAC/C;AAEA;;;CAGC,GACD,eAAenB,cACbf,OAAwB,EACxBa,QAAuB;IAEvB,MAAM+B,OAAOvD,QAAQ,6BAA6BwD,KAAK;IAEvD,IAAI/B;IACJ,IAAI;QACFA,aAAa,MAAMd,QAAQe,aAAa;IAC1C,EAAE,OAAOY,OAAO;QACdiB,KAAKG,IAAI;QACTlD,cAAc,qCAAqC8B;QACnDd,SAASsC,MAAM,CAAC;YACd9B,SAAS,CAAC,mCAAmC,EAAElC,gBAAgBwC,QAAQ;YACvED,QAAQ;QACV;QACA,OAAO;IACT;IAEAkB,IAAI,CAAC9B,WAAWM,IAAI,KAAK,UAAU,YAAY,OAAO;IACtDP,SAASsC,MAAM,CAAC1D,uBAAuBqB;IACvC,OAAOA;AACT;AAEA,SAAS2B,uBAAuBnB,MAAsB;IACpD,IAAIA,OAAO8B,OAAO,KAAK,UAAU;QAC/B,OAAO,CAAC,yCAAyC,EAAErE,UACjD,UACAuC,OAAO+B,KAAK,IAAI,YAChB;QACE,EAAEtE,UAAU,OAAO,QAAQ,sBAAsB,CAAC;IACxD;IAEA,IAAIuC,OAAOF,IAAI,KAAK,WAAW;QAC7B,OAAO,CAAC;;WAED,EAAErC,UAAU,UAAUuC,OAAO+B,KAAK,IAAI,0BAA0B;WAChE,EAAEtE,UAAU,UAAUuC,OAAOgC,EAAE,EAAE;;;;QAIpC,EAAEvE,UAAU,OAAO,QAAQ,sBAAsB,CAAC;IACxD;IAEA,OAAO,CAAC,mBAAmB,EAAEA,UAAU,UAAUuC,OAAOiC,GAAG,IAAIjC,OAAOgC,EAAE,EAAE;QACpE,EAAEvE,UAAU,OAAO,QAAQ,sBAAsB,CAAC;AAC1D;AAEA,SAASkE,gBAAgB3B,MAAsB,EAAEpB,MAAc;IAC7D,IAAIoB,OAAO8B,OAAO,KAAK,UAAU;QAC/BlD,OAAOI,GAAG,CAAC,CAAC,EAAE,EAAEvB,UAAU,QAAQ,oBAAoB;QACtD;IACF;IAEA,IAAIuC,OAAOF,IAAI,KAAK,WAAW;QAC7BlB,OAAOI,GAAG,CACR,CAAC,EAAE,EAAEvB,UAAU,QAAQ,sCAAsC,iCAAiC,EAC5FuC,OAAO+B,KAAK,GAAGtE,UAAU,UAAU,CAAC,CAAC,EAAEuC,OAAO+B,KAAK,CAAC,CAAC,CAAC,IAAI,mBAC3D,gBAAgB,CAAC;QAEpBnD,OAAOI,GAAG,CACR,CAAC,EAAE,EAAEvB,UAAU,QAAQ,6EAA6E,kCAAkC,CAAC;QAEzI;IACF;IAEAmB,OAAOI,GAAG,CACR,CAAC,6DAA6D,EAAEgB,OAAOiC,GAAG,IAAI,cAAc,gBAAgB,CAAC;AAEjH;AAEA,sFAAsF,GACtF,SAAStB,iBACPN,KAAc,EACdP,IAA6B;IAE7B,yDAAyD;IACzD,IAAIO,iBAAiB6B,SAAS7B,MAAM8B,IAAI,KAAK,mBAAmB;QAC9D,OAAO;YAAC7B,MAAM3C,UAAUyD,UAAU;YAAErB,SAAS;QAAoB;IACnE;IACA,+DAA+D;IAC/D,IAAIM,iBAAiB3C,UAAU;QAC7B,OAAO;YAAC4C,MAAMD,MAAM+B,KAAK,EAAE9B,QAAQ;YAAGP,SAASM,MAAMN,OAAO;QAAA;IAC9D;IACA,MAAMsB,QAAQvB,SAAS,YAAY,gBAAgB;IACnDvB,cAAc,CAAC,kBAAkB,EAAE8C,OAAO,EAAEhB;IAC5C,OAAO;QAACC,MAAM;QAAGP,SAAS,CAAC,kBAAkB,EAAEsB,MAAM,EAAE,EAAExD,gBAAgBwC,QAAQ;IAAA;AACnF"}
@@ -1,6 +1,6 @@
1
1
  import { styleText } from 'node:util';
2
2
  import { Args } from '@oclif/core';
3
- import { SanityCommand, subdebug } from '@sanity/cli-core';
3
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
4
4
  import { select } from '@sanity/cli-core/ux';
5
5
  import { assertDatasetExists } from '../../actions/backup/assertDatasetExist.js';
6
6
  import { promptForProject } from '../../prompts/promptForProject.js';
@@ -69,6 +69,10 @@ export class DisableBackupCommand extends SanityCommand {
69
69
  }
70
70
  if (dataset) {
71
71
  assertDatasetExists(datasets, dataset, this.output);
72
+ } else if (this.isUnattended()) {
73
+ this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {
74
+ exit: exitCodes.USAGE_ERROR
75
+ });
72
76
  } else {
73
77
  dataset = await this.promptForDataset(datasets);
74
78
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/backups/disable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {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 {setBackup} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst disableBackupDebug = subdebug('backup:disable')\n\nexport class DisableBackupCommand extends SanityCommand<typeof DisableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to disable backup for',\n required: false,\n }),\n }\n\n static override description = 'Disable backup for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively disable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Disable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to disable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:disable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(DisableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n disableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n try {\n await setBackup({dataset, projectId, status: false})\n\n this.log(`${styleText('green', `Disabled daily backups for dataset ${dataset}.\\n`)}`)\n this.log(\n `${styleText('yellow', 'Note: Existing backups will be retained according to your retention policy.\\n')}`,\n )\n\n disableBackupDebug(`Successfully disabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n disableBackupDebug(`Failed to disable backup for dataset`, error)\n this.error(`Disabling dataset backup failed: ${message}`, {exit: 1})\n }\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 const err = error as Error\n disableBackupDebug(`Error fetching datasets`, err)\n this.error(`Failed to fetch datasets:\\n${err.message}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Args","SanityCommand","subdebug","select","assertDatasetExists","promptForProject","setBackup","listDatasets","getProjectIdFlag","disableBackupDebug","DisableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","length","output","promptForDataset","status","log","choices","map","name","value","err"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/backups/disable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} 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 {setBackup} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst disableBackupDebug = subdebug('backup:disable')\n\nexport class DisableBackupCommand extends SanityCommand<typeof DisableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to disable backup for',\n required: false,\n }),\n }\n\n static override description = 'Disable backup for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively disable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Disable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to disable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:disable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(DisableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n disableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n try {\n await setBackup({dataset, projectId, status: false})\n\n this.log(`${styleText('green', `Disabled daily backups for dataset ${dataset}.\\n`)}`)\n this.log(\n `${styleText('yellow', 'Note: Existing backups will be retained according to your retention policy.\\n')}`,\n )\n\n disableBackupDebug(`Successfully disabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n disableBackupDebug(`Failed to disable backup for dataset`, error)\n this.error(`Disabling dataset backup failed: ${message}`, {exit: 1})\n }\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 const err = error as Error\n disableBackupDebug(`Error fetching datasets`, err)\n this.error(`Failed to fetch datasets:\\n${err.message}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Args","exitCodes","SanityCommand","subdebug","select","assertDatasetExists","promptForProject","setBackup","listDatasets","getProjectIdFlag","disableBackupDebug","DisableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","length","output","isUnattended","USAGE_ERROR","promptForDataset","status","log","choices","map","name","value","err"],"mappings":"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"}
@@ -5,7 +5,7 @@ import path from 'node:path';
5
5
  import { finished } from 'node:stream/promises';
6
6
  import { styleText } from 'node:util';
7
7
  import { Args, Flags } from '@oclif/core';
8
- import { SanityCommand } from '@sanity/cli-core';
8
+ import { exitCodes, SanityCommand } from '@sanity/cli-core';
9
9
  import { boxen, confirm, input, select } from '@sanity/cli-core/ux';
10
10
  import pMap from 'p-map';
11
11
  import prettyMs from 'pretty-ms';
@@ -22,6 +22,7 @@ import { promptForDataset } from '../../prompts/promptForDataset.js';
22
22
  import { promptForProject } from '../../prompts/promptForProject.js';
23
23
  import { listBackups } from '../../services/backup.js';
24
24
  import { listDatasets } from '../../services/datasets.js';
25
+ import { formatCliErrorMessages } from '../../util/formatCliErrorMessages.js';
25
26
  import { humanFileSize } from '../../util/humanFileSize.js';
26
27
  import { isPathDirName } from '../../util/isPathDirName.js';
27
28
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
@@ -77,8 +78,22 @@ export class DownloadBackupCommand extends SanityCommand {
77
78
  'backup:download'
78
79
  ];
79
80
  async run() {
80
- const { args } = await this.parse(DownloadBackupCommand);
81
+ const { args, flags } = await this.parse(DownloadBackupCommand);
81
82
  let { dataset } = args;
83
+ if (this.isUnattended()) {
84
+ const errors = [];
85
+ if (!dataset) {
86
+ errors.push('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.');
87
+ }
88
+ if (!flags['backup-id']) {
89
+ errors.push('Backup ID is required in unattended mode. Pass it with `--backup-id <id>`.');
90
+ }
91
+ if (errors.length > 0) {
92
+ this.error(formatCliErrorMessages(errors), {
93
+ exit: exitCodes.USAGE_ERROR
94
+ });
95
+ }
96
+ }
82
97
  const projectId = await this.getProjectId({
83
98
  fallback: ()=>promptForProject({
84
99
  requiredPermissions: [
@@ -219,8 +234,12 @@ ${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`, {
219
234
  return path.resolve(this.flags.out);
220
235
  }
221
236
  const workDir = process.cwd();
237
+ const defaultPath = path.join(workDir, defaultOutFileName);
238
+ if (this.isUnattended()) {
239
+ return defaultPath;
240
+ }
222
241
  const inputResult = await input({
223
- default: path.join(workDir, defaultOutFileName),
242
+ default: defaultPath,
224
243
  message: 'Output path:'
225
244
  });
226
245
  return path.resolve(inputResult);
@@ -234,8 +253,8 @@ ${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`, {
234
253
  }
235
254
  const backupId = String(this.flags['backup-id'] || await this.promptForBackupId(projectId, datasetName));
236
255
  if ('concurrency' in this.flags && (this.flags.concurrency < 1 || this.flags.concurrency > MAX_DOWNLOAD_CONCURRENCY)) {
237
- this.error(`concurrency should be in 1 to ${MAX_DOWNLOAD_CONCURRENCY} range`, {
238
- exit: 1
256
+ this.error(`\`--concurrency\` must be between 1 and ${MAX_DOWNLOAD_CONCURRENCY}.`, {
257
+ exit: exitCodes.USAGE_ERROR
239
258
  });
240
259
  }
241
260
  const defaultOutFileName = `${datasetName}-backup-${backupId}.tar.gz`;
@@ -247,6 +266,11 @@ ${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`, {
247
266
  const exists = await access(out).then(()=>true, ()=>false);
248
267
  // If the file already exists, ask for confirmation if it should be overwritten.
249
268
  if (!this.flags.overwrite && exists) {
269
+ if (this.isUnattended()) {
270
+ this.error(`File "${out}" already exists. Pass \`--overwrite\` to replace it.`, {
271
+ exit: exitCodes.USAGE_ERROR
272
+ });
273
+ }
250
274
  const shouldOverwrite = await confirm({
251
275
  default: false,
252
276
  message: `File "${out}" already exists, would you like to overwrite it?`
@@ -254,7 +278,7 @@ ${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`, {
254
278
  // If the user does not want to overwrite the file, cancel the operation.
255
279
  if (!shouldOverwrite) {
256
280
  this.error('Operation cancelled.', {
257
- exit: 1
281
+ exit: exitCodes.USER_ABORT
258
282
  });
259
283
  }
260
284
  }