@sanity/cli 8.21.0 → 8.22.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 (74) hide show
  1. package/README.md +106 -47
  2. package/codemods/dashboard.js +111 -0
  3. package/dist/actions/build/buildApp.js +1 -1
  4. package/dist/actions/build/buildApp.js.map +1 -1
  5. package/dist/actions/build/buildStudio.js +1 -1
  6. package/dist/actions/build/buildStudio.js.map +1 -1
  7. package/dist/actions/codemods/dashboard.js +113 -0
  8. package/dist/actions/codemods/dashboard.js.map +1 -0
  9. package/dist/actions/codemods/index.js +2 -0
  10. package/dist/actions/codemods/index.js.map +1 -1
  11. package/dist/actions/codemods/types.js.map +1 -1
  12. package/dist/actions/deploy/deployApp.js +13 -6
  13. package/dist/actions/deploy/deployApp.js.map +1 -1
  14. package/dist/actions/deploy/deployChecks.js +12 -7
  15. package/dist/actions/deploy/deployChecks.js.map +1 -1
  16. package/dist/actions/deploy/deployRunner.js +14 -8
  17. package/dist/actions/deploy/deployRunner.js.map +1 -1
  18. package/dist/actions/deploy/deployStudio.js +39 -4
  19. package/dist/actions/deploy/deployStudio.js.map +1 -1
  20. package/dist/actions/deploy/resolveDeployTarget.js +7 -6
  21. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  22. package/dist/actions/init/project/promptForAppTemplateSetup.js +22 -0
  23. package/dist/actions/init/project/promptForAppTemplateSetup.js.map +1 -1
  24. package/dist/actions/schema/listSchemas.js +2 -3
  25. package/dist/actions/schema/listSchemas.js.map +1 -1
  26. package/dist/actions/typegen/generateResourceTypes.js +11 -8
  27. package/dist/actions/typegen/generateResourceTypes.js.map +1 -1
  28. package/dist/actions/typegen/watchResourceTypes.js +208 -0
  29. package/dist/actions/typegen/watchResourceTypes.js.map +1 -0
  30. package/dist/actions/undeploy/runUndeploy.js +14 -8
  31. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  32. package/dist/commands/codemod.js +15 -4
  33. package/dist/commands/codemod.js.map +1 -1
  34. package/dist/commands/context/get.js +3 -8
  35. package/dist/commands/context/get.js.map +1 -1
  36. package/dist/commands/context/imports/download.js +3 -8
  37. package/dist/commands/context/imports/download.js.map +1 -1
  38. package/dist/commands/context/imports/get.js +3 -8
  39. package/dist/commands/context/imports/get.js.map +1 -1
  40. package/dist/commands/context/imports/list.js +3 -8
  41. package/dist/commands/context/imports/list.js.map +1 -1
  42. package/dist/commands/context/jobs/get.js +8 -8
  43. package/dist/commands/context/jobs/get.js.map +1 -1
  44. package/dist/commands/context/list.js +2 -6
  45. package/dist/commands/context/list.js.map +1 -1
  46. package/dist/commands/deploy.js +1 -0
  47. package/dist/commands/deploy.js.map +1 -1
  48. package/dist/commands/doctor.js +2 -1
  49. package/dist/commands/doctor.js.map +1 -1
  50. package/dist/commands/openapi/list.js +2 -4
  51. package/dist/commands/openapi/list.js.map +1 -1
  52. package/dist/commands/projects/create.js +3 -6
  53. package/dist/commands/projects/create.js.map +1 -1
  54. package/dist/commands/schemas/list.js +1 -3
  55. package/dist/commands/schemas/list.js.map +1 -1
  56. package/dist/commands/tokens/create.js +2 -5
  57. package/dist/commands/tokens/create.js.map +1 -1
  58. package/dist/commands/tokens/list.js +2 -6
  59. package/dist/commands/tokens/list.js.map +1 -1
  60. package/dist/commands/tokens/rotate.js +2 -5
  61. package/dist/commands/tokens/rotate.js.map +1 -1
  62. package/dist/commands/typegen/generate.js +83 -45
  63. package/dist/commands/typegen/generate.js.map +1 -1
  64. package/dist/commands/undeploy.js +1 -0
  65. package/dist/commands/undeploy.js.map +1 -1
  66. package/dist/commands/whoami.js +5 -11
  67. package/dist/commands/whoami.js.map +1 -1
  68. package/dist/exports/invokeSanityCli/index.js +26 -6
  69. package/dist/exports/invokeSanityCli/index.js.map +1 -1
  70. package/dist/generated/apiRoutes.js +11 -26
  71. package/dist/generated/apiRoutes.js.map +1 -1
  72. package/dist/telemetry/init.telemetry.js.map +1 -1
  73. package/oclif.manifest.json +1323 -1294
  74. package/package.json +6 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/typegen/generateResourceTypes.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport {isAbsolute, join} from 'node:path'\n\nimport {type TypeGenResource} from '@sanity/cli-core/types'\nimport {\n type GenerationResult,\n runTypegenGenerate,\n type TypeGenConfig,\n type TypegenProgressEvent,\n} from '@sanity/codegen'\n\nimport {type ExtractedSchema, fetchResourceSchema} from './fetchResourceSchema.js'\nimport {getResourceKey} from './parseTypegenResources.js'\n\n/**\n * A resource whose schema has been located, ready to generate.\n */\nexport type ResolvedResource =\n | {key: string; resource: TypeGenResource; schema: ExtractedSchema; schemaVersion: string}\n | {key: string; resource: TypeGenResource; schemaPath: string}\n\n/**\n * Locates the schema for every resource before anything is generated.\n *\n * A resource with a `schema` path uses that file, which must exist and parse. One without\n * fetches the schema bound to its dataset. Doing this first means a missing binding or a\n * missing or malformed file fails the run before any output is written.\n */\nexport async function resolveResourceSchemas(options: {\n /** Called once a resource's schema is located. */\n onResolved?: (resolved: ResolvedResource) => void\n /** Called before a resource's schema is located. */\n onResolving?: (resource: TypeGenResource, key: string) => void\n resources: TypeGenResource[]\n workDir: string\n}): Promise<ResolvedResource[]> {\n const {onResolved, onResolving, resources, workDir} = options\n const resolved: ResolvedResource[] = []\n\n for (const resource of resources) {\n const key = getResourceKey(resource)\n onResolving?.(resource, key)\n let entry: ResolvedResource\n\n if (resource.schema === undefined) {\n try {\n entry = {key, resource, ...(await fetchResourceSchema(resource))}\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(`Failed to get the schema for \"${key}\": ${message}`, {cause: error})\n }\n } else {\n const schemaPath = isAbsolute(resource.schema)\n ? resource.schema\n : join(workDir, resource.schema)\n await assertExtractedSchemaFile(schemaPath, {key, schema: resource.schema})\n // Codegen reads the file again, so the generated output names it as its source, as it\n // does without resources.\n entry = {key, resource, schemaPath: resource.schema}\n }\n\n resolved.push(entry)\n onResolved?.(entry)\n }\n\n return resolved\n}\n\n/**\n * Generates one output file per resource, registering its types under the resource key.\n */\nexport async function generateResourceTypes(options: {\n /** Settings shared by every resource. */\n config: TypeGenConfig\n /** Returns the progress handler for one resource's generation. */\n onProgress?: (resolved: ResolvedResource) => ((event: TypegenProgressEvent) => void) | undefined\n resolved: ResolvedResource[]\n workDir: string\n}): Promise<{key: string; result: GenerationResult}[]> {\n const {config, onProgress, resolved, workDir} = options\n const results: {key: string; result: GenerationResult}[] = []\n\n for (const entry of resolved) {\n const {dataset, generates, path, projectId} = entry.resource\n let result: GenerationResult\n try {\n result = await runTypegenGenerate({\n config: {\n ...config,\n generates,\n path: path ?? config.path,\n schema: 'schemaPath' in entry ? entry.schemaPath : config.schema,\n },\n extractedSchema: 'schema' in entry ? entry.schema : undefined,\n onProgress: onProgress?.(entry),\n resource: {dataset, projectId},\n workDir,\n })\n } catch (error) {\n // Codegen writes a resource's file once its generation succeeds, so the files of this\n // resource and the ones after it are unchanged. Files of earlier resources are not\n // rolled back.\n const message = error instanceof Error ? error.message : String(error)\n const written = results.map(({key}) => `\"${key}\"`).join(', ')\n throw new Error(\n `Failed to generate types for \"${entry.key}\": ${message}` +\n (written\n ? `\\nTypes for ${written} were updated. The other output files are unchanged.`\n : ''),\n {cause: error},\n )\n }\n results.push({key: entry.key, result})\n }\n\n return results\n}\n\n/**\n * Checks that a local schema file exists and holds an extracted schema, which is a JSON array.\n */\nasync function assertExtractedSchemaFile(\n schemaPath: string,\n {key, schema}: {key: string; schema: string},\n): Promise<void> {\n let content: string\n try {\n content = await readFile(schemaPath, 'utf8')\n } catch {\n throw new Error(\n `Schema file not found for \"${key}\": ${schema}. Run \"sanity schema extract\" to create it.`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(content)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(`Schema file for \"${key}\" is not valid JSON: ${schema}. ${message}`, {\n cause: error,\n })\n }\n\n if (!Array.isArray(parsed)) {\n throw new TypeError(\n `Schema file for \"${key}\" is not an extracted schema: ${schema}. Run \"sanity schema extract\" to recreate it.`,\n )\n }\n}\n"],"names":["readFile","isAbsolute","join","runTypegenGenerate","fetchResourceSchema","getResourceKey","resolveResourceSchemas","options","onResolved","onResolving","resources","workDir","resolved","resource","key","entry","schema","undefined","error","message","Error","String","cause","schemaPath","assertExtractedSchemaFile","push","generateResourceTypes","config","onProgress","results","dataset","generates","path","projectId","result","extractedSchema","written","map","content","parsed","JSON","parse","Array","isArray","TypeError"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/typegen/generateResourceTypes.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport {isAbsolute, join} from 'node:path'\n\nimport {type TypeGenResource} from '@sanity/cli-core/types'\nimport {\n type GenerationResult,\n runTypegenGenerate,\n type TypeGenConfig,\n type TypegenProgressEvent,\n} from '@sanity/codegen'\n\nimport {type ExtractedSchema, fetchResourceSchema} from './fetchResourceSchema.js'\nimport {getResourceKey} from './parseTypegenResources.js'\n\n/**\n * A resource whose schema has been located, ready to generate.\n */\nexport type ResolvedResource =\n | {key: string; resource: TypeGenResource; schema: ExtractedSchema; schemaVersion: string}\n | {key: string; resource: TypeGenResource; schemaPath: string}\n\n/**\n * Locates the schema for every resource before anything is generated.\n *\n * A resource with a `schema` path uses that file, which must exist and parse unless\n * `checkSchemaFiles` is `false`. One without fetches the schema bound to its dataset. Doing this\n * first means a missing binding or a missing or malformed file fails the run before any output is\n * written.\n */\nexport async function resolveResourceSchemas(options: {\n /**\n * Whether a local schema file must exist and parse now. Watch mode passes `false`, because it\n * generates once the file appears, as it does without resources.\n * @defaultValue true\n */\n checkSchemaFiles?: boolean\n /** Called once a resource's schema is located. */\n onResolved?: (resolved: ResolvedResource) => void\n /** Called before a resource's schema is located. */\n onResolving?: (resource: TypeGenResource, key: string) => void\n resources: TypeGenResource[]\n workDir: string\n}): Promise<ResolvedResource[]> {\n const {checkSchemaFiles = true, onResolved, onResolving, resources, workDir} = options\n const resolved: ResolvedResource[] = []\n\n for (const resource of resources) {\n const key = getResourceKey(resource)\n onResolving?.(resource, key)\n let entry: ResolvedResource\n\n if (resource.schema === undefined) {\n try {\n entry = {key, resource, ...(await fetchResourceSchema(resource))}\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(`Failed to get the schema for \"${key}\": ${message}`, {cause: error})\n }\n } else {\n const schemaPath = isAbsolute(resource.schema)\n ? resource.schema\n : join(workDir, resource.schema)\n if (checkSchemaFiles) {\n await assertExtractedSchemaFile(schemaPath, {key, schema: resource.schema})\n }\n // Codegen reads the file again, so the generated output names it as its source, as it\n // does without resources.\n entry = {key, resource, schemaPath: resource.schema}\n }\n\n resolved.push(entry)\n onResolved?.(entry)\n }\n\n return resolved\n}\n\n/**\n * Generates one output file per resource, registering its types under the resource key.\n */\nexport async function generateResourceTypes(options: {\n /** Settings shared by every resource. */\n config: TypeGenConfig\n /** Returns the progress handler for one resource's generation. */\n onProgress?: (resolved: ResolvedResource) => ((event: TypegenProgressEvent) => void) | undefined\n resolved: ResolvedResource[]\n workDir: string\n}): Promise<{key: string; result: GenerationResult}[]> {\n const {config, onProgress, resolved, workDir} = options\n const results: {key: string; result: GenerationResult}[] = []\n\n for (const entry of resolved) {\n const {dataset, generates, path, projectId} = entry.resource\n let result: GenerationResult\n try {\n result = await runTypegenGenerate({\n config: {\n ...config,\n generates,\n path: path ?? config.path,\n schema: 'schemaPath' in entry ? entry.schemaPath : config.schema,\n },\n extractedSchema: 'schema' in entry ? entry.schema : undefined,\n onProgress: onProgress?.(entry),\n resource: {dataset, projectId},\n workDir,\n })\n } catch (error) {\n // Codegen writes a resource's file once its generation succeeds, so the files of this\n // resource and the ones after it are unchanged. Files of earlier resources are not\n // rolled back.\n const message = error instanceof Error ? error.message : String(error)\n const written = results.map(({key}) => `\"${key}\"`).join(', ')\n throw new Error(\n `Failed to generate types for \"${entry.key}\": ${message}` +\n (written\n ? `\\nTypes for ${written} were updated. The other output files are unchanged.`\n : ''),\n {cause: error},\n )\n }\n results.push({key: entry.key, result})\n }\n\n return results\n}\n\n/**\n * Checks that a local schema file exists and holds an extracted schema, which is a JSON array.\n */\nasync function assertExtractedSchemaFile(\n schemaPath: string,\n {key, schema}: {key: string; schema: string},\n): Promise<void> {\n let content: string\n try {\n content = await readFile(schemaPath, 'utf8')\n } catch {\n throw new Error(\n `Schema file not found for \"${key}\": ${schema}. Run \"sanity schema extract\" to create it.`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(content)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(`Schema file for \"${key}\" is not valid JSON: ${schema}. ${message}`, {\n cause: error,\n })\n }\n\n if (!Array.isArray(parsed)) {\n throw new TypeError(\n `Schema file for \"${key}\" is not an extracted schema: ${schema}. Run \"sanity schema extract\" to recreate it.`,\n )\n }\n}\n"],"names":["readFile","isAbsolute","join","runTypegenGenerate","fetchResourceSchema","getResourceKey","resolveResourceSchemas","options","checkSchemaFiles","onResolved","onResolving","resources","workDir","resolved","resource","key","entry","schema","undefined","error","message","Error","String","cause","schemaPath","assertExtractedSchemaFile","push","generateResourceTypes","config","onProgress","results","dataset","generates","path","projectId","result","extractedSchema","written","map","content","parsed","JSON","parse","Array","isArray","TypeError"],"mappings":"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"}
@@ -0,0 +1,208 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { basename, relative, resolve, sep } from 'node:path';
3
+ import { watch } from 'chokidar';
4
+ import debounce from 'lodash-es/debounce.js';
5
+ import mean from 'lodash-es/mean.js';
6
+ import picomatch from 'picomatch';
7
+ import { toForwardSlashes } from '../../util/toForwardSlashes.js';
8
+ import { generateResourceTypes } from './generateResourceTypes.js';
9
+ /** Delay after the last change before generating again, as in watch mode without resources. */ const DEBOUNCE_MS = 1000;
10
+ /** Directories that are never watched. Codegen's query scan skips `node_modules` as well. */ const IGNORED_DIRECTORIES = new Set([
11
+ '.git',
12
+ 'node_modules'
13
+ ]);
14
+ /** How often to check that every watched base directory and schema file still exists. */ const EXISTENCE_CHECK_MS = 1000;
15
+ /**
16
+ * Generates types for every resource, then generates them again whenever a query file or a local
17
+ * schema file changes.
18
+ *
19
+ * The caller resolves the schemas, once. A schema fetched from a dataset's binding is used until
20
+ * the watcher stops, so a schema deployed while it runs is not picked up until it restarts. A
21
+ * local schema file is read again on every run, and a missing one fails only that resource's
22
+ * generation until the file appears.
23
+ *
24
+ * Each resource generates on its own, so a broken schema or query in one does not hold back the
25
+ * others. Generated files are not watched: outputs usually sit inside the query pattern, such as
26
+ * `./src/sanity.types.production.ts` under `./src/**`, and writing one would start the next run.
27
+ */ export function watchResourceTypes(options) {
28
+ const { config, onChange, onError, onProgress, resolved, workDir } = options;
29
+ const queryMatchers = resolved.map(({ resource })=>createQueryMatcher(resource.path ?? config.path, workDir));
30
+ const schemaFiles = new Set(resolved.flatMap((entry)=>'schemaPath' in entry ? [
31
+ resolve(workDir, entry.schemaPath)
32
+ ] : []));
33
+ // Lowercased, because two names that differ only in case are the same file on the default
34
+ // macOS and Windows file systems.
35
+ const outputFiles = new Set(resolved.map(({ resource })=>resolve(workDir, resource.generates).toLowerCase()));
36
+ const roots = [
37
+ ...new Set([
38
+ ...queryMatchers.flatMap(({ bases })=>bases),
39
+ ...schemaFiles
40
+ ])
41
+ ];
42
+ const startTime = Date.now();
43
+ const successfulDurations = [];
44
+ let failedCount = 0;
45
+ let activeRun;
46
+ let isPending = false;
47
+ let isStopped = false;
48
+ async function generateEach() {
49
+ for (const entry of resolved){
50
+ if (isStopped) return;
51
+ const generationStartTime = Date.now();
52
+ try {
53
+ await generateResourceTypes({
54
+ config,
55
+ onProgress,
56
+ resolved: [
57
+ entry
58
+ ],
59
+ workDir
60
+ });
61
+ successfulDurations.push(Date.now() - generationStartTime);
62
+ } catch (error) {
63
+ failedCount++;
64
+ onError?.(error);
65
+ }
66
+ }
67
+ }
68
+ // Runs one generation at a time. Changes during a run queue a single follow-up run.
69
+ async function runGeneration() {
70
+ if (isStopped) return;
71
+ if (activeRun) {
72
+ isPending = true;
73
+ return;
74
+ }
75
+ isPending = false;
76
+ activeRun = generateEach();
77
+ try {
78
+ await activeRun;
79
+ } finally{
80
+ activeRun = undefined;
81
+ }
82
+ if (isPending) await runGeneration();
83
+ }
84
+ const scheduleGeneration = debounce(()=>void runGeneration(), DEBOUNCE_MS);
85
+ const missingRoots = new Set(roots.filter((root)=>!existsSync(root)));
86
+ const watcher = watch(roots.filter((root)=>!missingRoots.has(root)), {
87
+ ignored: (path)=>IGNORED_DIRECTORIES.has(basename(path)) || outputFiles.has(resolve(path).toLowerCase()),
88
+ ignoreInitial: true
89
+ });
90
+ // chokidar stops watching a path once it is removed, as when switching git branches, and does
91
+ // not reliably notice it come back. A watch set up while its directory is removed and created
92
+ // again also fails without an error. So the watcher checks every root's existence itself. A root
93
+ // that is missing is watched again once it exists, and once more on the next check in case that
94
+ // watch failed. Every resource is then generated, which also picks up files written before the
95
+ // new watch was ready.
96
+ let addedOnLastCheck = [];
97
+ const existenceCheck = setInterval(()=>{
98
+ const added = [];
99
+ for (const root of roots){
100
+ if (!existsSync(root)) {
101
+ missingRoots.add(root);
102
+ } else if (missingRoots.delete(root)) {
103
+ watcher.add(root);
104
+ added.push(root);
105
+ onChange?.('add', toForwardSlashes(relative(workDir, root)));
106
+ scheduleGeneration();
107
+ } else if (addedOnLastCheck.includes(root)) {
108
+ watcher.add(root);
109
+ }
110
+ }
111
+ addedOnLastCheck = added;
112
+ }, EXISTENCE_CHECK_MS);
113
+ watcher.on('all', (event, filePath)=>{
114
+ const absolutePath = resolve(workDir, filePath);
115
+ // A root removed and created again between two existence checks is watched again too.
116
+ if (event === 'unlink' || event === 'unlinkDir') {
117
+ for (const root of roots){
118
+ if (root === absolutePath || root.startsWith(absolutePath + sep)) {
119
+ missingRoots.add(root);
120
+ }
121
+ }
122
+ }
123
+ if (event !== 'add' && event !== 'change' && event !== 'unlink') return;
124
+ if (outputFiles.has(absolutePath.toLowerCase())) return;
125
+ const relativePath = toForwardSlashes(relative(workDir, absolutePath));
126
+ if (!schemaFiles.has(absolutePath) && !queryMatchers.some(({ isMatch })=>isMatch(relativePath))) {
127
+ return;
128
+ }
129
+ onChange?.(event, relativePath);
130
+ scheduleGeneration();
131
+ });
132
+ watcher.on('error', (error)=>{
133
+ const message = error instanceof Error ? error.message : String(error);
134
+ onError?.(new Error(`Watcher error: ${message}`, {
135
+ cause: error
136
+ }));
137
+ });
138
+ void runGeneration();
139
+ return {
140
+ getStats: ()=>({
141
+ averageGenerationDuration: mean(successfulDurations) || 0,
142
+ generationFailedCount: failedCount,
143
+ generationSuccessfulCount: successfulDurations.length,
144
+ watcherDuration: Date.now() - startTime
145
+ }),
146
+ stop: async ()=>{
147
+ isStopped = true;
148
+ clearInterval(existenceCheck);
149
+ scheduleGeneration.cancel();
150
+ await watcher.close();
151
+ await activeRun;
152
+ }
153
+ };
154
+ }
155
+ /**
156
+ * Matches paths relative to `workDir` against one resource's query patterns, as codegen's query
157
+ * scan reads them with globby:
158
+ *
159
+ * - A pattern starting with `!` excludes files from the patterns listed before it.
160
+ * - A pattern without glob characters names a file or a directory, and a directory includes or
161
+ * excludes everything inside it. `.` names `workDir`.
162
+ *
163
+ * chokidar 4 and later do not expand globs, so `bases` holds each pattern's static base directory
164
+ * to watch.
165
+ */ function createQueryMatcher(path, workDir) {
166
+ const includes = [];
167
+ for (const pattern of Array.isArray(path) ? path : [
168
+ path
169
+ ]){
170
+ const isExclude = pattern.startsWith('!');
171
+ const body = isExclude ? pattern.slice(1) : pattern;
172
+ if (!body) continue;
173
+ const patterns = normalizePattern(body, workDir);
174
+ if (isExclude) {
175
+ for (const include of includes)include.excludes.push(...patterns);
176
+ } else {
177
+ includes.push({
178
+ excludes: [],
179
+ patterns
180
+ });
181
+ }
182
+ }
183
+ const matchers = includes.map(({ excludes, patterns })=>picomatch(patterns, excludes.length > 0 ? {
184
+ ignore: excludes
185
+ } : {}));
186
+ return {
187
+ bases: includes.map(({ patterns })=>resolve(workDir, picomatch.scan(patterns[0]).base)),
188
+ isMatch: (relativePath)=>matchers.some((isMatch)=>isMatch(relativePath))
189
+ };
190
+ }
191
+ /**
192
+ * Returns picomatch patterns relative to `workDir` with forward slashes, with `.` and `..`
193
+ * segments resolved, to compare with event paths. A pattern without glob characters also matches
194
+ * everything inside it.
195
+ */ function normalizePattern(pattern, workDir) {
196
+ const normalized = toForwardSlashes(relative(workDir, resolve(workDir, pattern)));
197
+ if (!normalized) return [
198
+ '**'
199
+ ];
200
+ return picomatch.scan(normalized).isGlob ? [
201
+ normalized
202
+ ] : [
203
+ normalized,
204
+ `${normalized}/**`
205
+ ];
206
+ }
207
+
208
+ //# sourceMappingURL=watchResourceTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/typegen/watchResourceTypes.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport {basename, relative, resolve, sep} from 'node:path'\n\nimport {type TypeGenConfig, type TypegenProgressEvent} from '@sanity/codegen'\nimport {watch} from 'chokidar'\nimport debounce from 'lodash-es/debounce.js'\nimport mean from 'lodash-es/mean.js'\nimport picomatch from 'picomatch'\n\nimport {toForwardSlashes} from '../../util/toForwardSlashes.js'\nimport {generateResourceTypes, type ResolvedResource} from './generateResourceTypes.js'\n\n/** Delay after the last change before generating again, as in watch mode without resources. */\nconst DEBOUNCE_MS = 1000\n\n/** Directories that are never watched. Codegen's query scan skips `node_modules` as well. */\nconst IGNORED_DIRECTORIES = new Set(['.git', 'node_modules'])\n\n/** How often to check that every watched base directory and schema file still exists. */\nconst EXISTENCE_CHECK_MS = 1000\n\n/**\n * A running watcher started by {@link watchResourceTypes}.\n */\nexport interface ResourceTypesWatcher {\n /** Statistics for the watch mode trace, counting each resource's generation. */\n getStats: () => {\n averageGenerationDuration: number\n generationFailedCount: number\n generationSuccessfulCount: number\n watcherDuration: number\n }\n /** Stops watching. Resolves once the resource generating at the time, if any, is written. */\n stop: () => Promise<void>\n}\n\n/**\n * Generates types for every resource, then generates them again whenever a query file or a local\n * schema file changes.\n *\n * The caller resolves the schemas, once. A schema fetched from a dataset's binding is used until\n * the watcher stops, so a schema deployed while it runs is not picked up until it restarts. A\n * local schema file is read again on every run, and a missing one fails only that resource's\n * generation until the file appears.\n *\n * Each resource generates on its own, so a broken schema or query in one does not hold back the\n * others. Generated files are not watched: outputs usually sit inside the query pattern, such as\n * `./src/sanity.types.production.ts` under `./src/**`, and writing one would start the next run.\n */\nexport function watchResourceTypes(options: {\n /** Settings shared by every resource. */\n config: TypeGenConfig\n /** Called for each change that schedules a run, with the path relative to `workDir`. */\n onChange?: (event: string, relativePath: string) => void\n /** Called when a resource's generation or the file watcher fails. The watcher keeps running. */\n onError?: (error: unknown) => void\n /** Returns the progress handler for one resource's generation. */\n onProgress?: (resolved: ResolvedResource) => ((event: TypegenProgressEvent) => void) | undefined\n resolved: ResolvedResource[]\n workDir: string\n}): ResourceTypesWatcher {\n const {config, onChange, onError, onProgress, resolved, workDir} = options\n\n const queryMatchers = resolved.map(({resource}) =>\n createQueryMatcher(resource.path ?? config.path, workDir),\n )\n const schemaFiles = new Set(\n resolved.flatMap((entry) =>\n 'schemaPath' in entry ? [resolve(workDir, entry.schemaPath)] : [],\n ),\n )\n // Lowercased, because two names that differ only in case are the same file on the default\n // macOS and Windows file systems.\n const outputFiles = new Set(\n resolved.map(({resource}) => resolve(workDir, resource.generates).toLowerCase()),\n )\n const roots = [...new Set([...queryMatchers.flatMap(({bases}) => bases), ...schemaFiles])]\n\n const startTime = Date.now()\n const successfulDurations: number[] = []\n let failedCount = 0\n let activeRun: Promise<void> | undefined\n let isPending = false\n let isStopped = false\n\n async function generateEach(): Promise<void> {\n for (const entry of resolved) {\n if (isStopped) return\n const generationStartTime = Date.now()\n try {\n await generateResourceTypes({config, onProgress, resolved: [entry], workDir})\n successfulDurations.push(Date.now() - generationStartTime)\n } catch (error) {\n failedCount++\n onError?.(error)\n }\n }\n }\n\n // Runs one generation at a time. Changes during a run queue a single follow-up run.\n async function runGeneration(): Promise<void> {\n if (isStopped) return\n if (activeRun) {\n isPending = true\n return\n }\n\n isPending = false\n activeRun = generateEach()\n try {\n await activeRun\n } finally {\n activeRun = undefined\n }\n\n if (isPending) await runGeneration()\n }\n\n const scheduleGeneration = debounce(() => void runGeneration(), DEBOUNCE_MS)\n\n const missingRoots = new Set(roots.filter((root) => !existsSync(root)))\n\n const watcher = watch(\n roots.filter((root) => !missingRoots.has(root)),\n {\n ignored: (path) =>\n IGNORED_DIRECTORIES.has(basename(path)) || outputFiles.has(resolve(path).toLowerCase()),\n ignoreInitial: true,\n },\n )\n\n // chokidar stops watching a path once it is removed, as when switching git branches, and does\n // not reliably notice it come back. A watch set up while its directory is removed and created\n // again also fails without an error. So the watcher checks every root's existence itself. A root\n // that is missing is watched again once it exists, and once more on the next check in case that\n // watch failed. Every resource is then generated, which also picks up files written before the\n // new watch was ready.\n let addedOnLastCheck: string[] = []\n const existenceCheck = setInterval(() => {\n const added: string[] = []\n for (const root of roots) {\n if (!existsSync(root)) {\n missingRoots.add(root)\n } else if (missingRoots.delete(root)) {\n watcher.add(root)\n added.push(root)\n onChange?.('add', toForwardSlashes(relative(workDir, root)))\n scheduleGeneration()\n } else if (addedOnLastCheck.includes(root)) {\n watcher.add(root)\n }\n }\n addedOnLastCheck = added\n }, EXISTENCE_CHECK_MS)\n\n watcher.on('all', (event, filePath) => {\n const absolutePath = resolve(workDir, filePath)\n\n // A root removed and created again between two existence checks is watched again too.\n if (event === 'unlink' || event === 'unlinkDir') {\n for (const root of roots) {\n if (root === absolutePath || root.startsWith(absolutePath + sep)) {\n missingRoots.add(root)\n }\n }\n }\n\n if (event !== 'add' && event !== 'change' && event !== 'unlink') return\n if (outputFiles.has(absolutePath.toLowerCase())) return\n\n const relativePath = toForwardSlashes(relative(workDir, absolutePath))\n if (\n !schemaFiles.has(absolutePath) &&\n !queryMatchers.some(({isMatch}) => isMatch(relativePath))\n ) {\n return\n }\n\n onChange?.(event, relativePath)\n scheduleGeneration()\n })\n\n watcher.on('error', (error) => {\n const message = error instanceof Error ? error.message : String(error)\n onError?.(new Error(`Watcher error: ${message}`, {cause: error}))\n })\n\n void runGeneration()\n\n return {\n getStats: () => ({\n averageGenerationDuration: mean(successfulDurations) || 0,\n generationFailedCount: failedCount,\n generationSuccessfulCount: successfulDurations.length,\n watcherDuration: Date.now() - startTime,\n }),\n stop: async () => {\n isStopped = true\n clearInterval(existenceCheck)\n scheduleGeneration.cancel()\n await watcher.close()\n await activeRun\n },\n }\n}\n\n/**\n * Matches paths relative to `workDir` against one resource's query patterns, as codegen's query\n * scan reads them with globby:\n *\n * - A pattern starting with `!` excludes files from the patterns listed before it.\n * - A pattern without glob characters names a file or a directory, and a directory includes or\n * excludes everything inside it. `.` names `workDir`.\n *\n * chokidar 4 and later do not expand globs, so `bases` holds each pattern's static base directory\n * to watch.\n */\nfunction createQueryMatcher(\n path: string | string[],\n workDir: string,\n): {bases: string[]; isMatch: (relativePath: string) => boolean} {\n const includes: {excludes: string[]; patterns: string[]}[] = []\n for (const pattern of Array.isArray(path) ? path : [path]) {\n const isExclude = pattern.startsWith('!')\n const body = isExclude ? pattern.slice(1) : pattern\n if (!body) continue\n const patterns = normalizePattern(body, workDir)\n if (isExclude) {\n for (const include of includes) include.excludes.push(...patterns)\n } else {\n includes.push({excludes: [], patterns})\n }\n }\n\n const matchers = includes.map(({excludes, patterns}) =>\n picomatch(patterns, excludes.length > 0 ? {ignore: excludes} : {}),\n )\n return {\n bases: includes.map(({patterns}) => resolve(workDir, picomatch.scan(patterns[0]).base)),\n isMatch: (relativePath) => matchers.some((isMatch) => isMatch(relativePath)),\n }\n}\n\n/**\n * Returns picomatch patterns relative to `workDir` with forward slashes, with `.` and `..`\n * segments resolved, to compare with event paths. A pattern without glob characters also matches\n * everything inside it.\n */\nfunction normalizePattern(pattern: string, workDir: string): string[] {\n const normalized = toForwardSlashes(relative(workDir, resolve(workDir, pattern)))\n if (!normalized) return ['**']\n return picomatch.scan(normalized).isGlob ? [normalized] : [normalized, `${normalized}/**`]\n}\n"],"names":["existsSync","basename","relative","resolve","sep","watch","debounce","mean","picomatch","toForwardSlashes","generateResourceTypes","DEBOUNCE_MS","IGNORED_DIRECTORIES","Set","EXISTENCE_CHECK_MS","watchResourceTypes","options","config","onChange","onError","onProgress","resolved","workDir","queryMatchers","map","resource","createQueryMatcher","path","schemaFiles","flatMap","entry","schemaPath","outputFiles","generates","toLowerCase","roots","bases","startTime","Date","now","successfulDurations","failedCount","activeRun","isPending","isStopped","generateEach","generationStartTime","push","error","runGeneration","undefined","scheduleGeneration","missingRoots","filter","root","watcher","has","ignored","ignoreInitial","addedOnLastCheck","existenceCheck","setInterval","added","add","delete","includes","on","event","filePath","absolutePath","startsWith","relativePath","some","isMatch","message","Error","String","cause","getStats","averageGenerationDuration","generationFailedCount","generationSuccessfulCount","length","watcherDuration","stop","clearInterval","cancel","close","pattern","Array","isArray","isExclude","body","slice","patterns","normalizePattern","include","excludes","matchers","ignore","scan","base","normalized","isGlob"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { styleText } from 'node:util';
2
2
  import { CLIError } from '@oclif/core/errors';
3
3
  import { exitCodes, subdebug } from '@sanity/cli-core';
4
- import { getErrorMessage } from '@sanity/cli-core/errors';
4
+ import { getErrorMessage, JsonOutputError } from '@sanity/cli-core/errors';
5
5
  import { confirm, spinner } from '@sanity/cli-core/ux';
6
6
  import { createCollectingReporter, createFailFastReporter } from '../../util/checks.js';
7
7
  import { toStderrOutput } from '../../util/toStderrOutput.js';
@@ -15,7 +15,8 @@ const undeployDebug = subdebug('undeploy');
15
15
  */ export async function runUndeploy(options, adapter) {
16
16
  const { flags, output } = options;
17
17
  const json = !!flags.json;
18
- const emitJson = (payload)=>output.log(JSON.stringify(payload, null, 2));
18
+ const emitJson = (payload)=>output.logJson(payload);
19
+ let failurePayload;
19
20
  // The JSON payload owns stdout, so the run's progress logs go to stderr; only
20
21
  // the final JSON.stringify writes to stdout.
21
22
  const runOptions = json ? {
@@ -32,8 +33,11 @@ const undeployDebug = subdebug('undeploy');
32
33
  target: resolution?.type === 'found' ? resolution.target : null,
33
34
  type: adapter.type
34
35
  };
35
- if (json) emitJson(undeployPlanToJson(plan));
36
- else renderUndeployPlan(plan, output);
36
+ if (json) {
37
+ const payload = undeployPlanToJson(plan);
38
+ if (plan.checks.some((check)=>check.status === 'fail')) failurePayload = payload;
39
+ else emitJson(payload);
40
+ } else renderUndeployPlan(plan, output);
37
41
  // A blocked plan exits like a real (fail-fast) undeploy would.
38
42
  const failed = plan.checks.find((check)=>check.status === 'fail');
39
43
  if (failed) output.error('Undeploy blocked by failing checks.', {
@@ -45,10 +49,9 @@ const undeployDebug = subdebug('undeploy');
45
49
  if (json && result) emitJson(result);
46
50
  } catch (error) {
47
51
  const failure = normalizeFailure(error, adapter.type);
48
- // A blocked dry run reaches this catch too (its exit throws) and already
49
- // printed its plan, so only a real undeploy adds the {undeployed: false} envelope.
52
+ // Preserve the blocked plan or the existing undeployment failure envelope.
50
53
  if (json && !flags['dry-run']) {
51
- emitJson({
54
+ failurePayload = {
52
55
  application: null,
53
56
  error: {
54
57
  message: failure.message
@@ -56,7 +59,10 @@ const undeployDebug = subdebug('undeploy');
56
59
  payload: null,
57
60
  reason: failure.message,
58
61
  undeployed: false
59
- });
62
+ };
63
+ }
64
+ if (json && failurePayload !== undefined) {
65
+ throw new JsonOutputError(failure.message, failurePayload, failure.exit);
60
66
  }
61
67
  output.error(failure.message, {
62
68
  exit: failure.exit
@@ -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)}`, {\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
+ {"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, JsonOutputError} 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.logJson(payload)\n let failurePayload: unknown\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) {\n const payload = undeployPlanToJson(plan)\n if (plan.checks.some((check) => check.status === 'fail')) failurePayload = payload\n else emitJson(payload)\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 // Preserve the blocked plan or the existing undeployment failure envelope.\n if (json && !flags['dry-run']) {\n failurePayload = {\n application: null,\n error: {message: failure.message},\n payload: null,\n reason: failure.message,\n undeployed: false,\n }\n }\n if (json && failurePayload !== undefined) {\n throw new JsonOutputError(failure.message, failurePayload, failure.exit)\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","JsonOutputError","confirm","spinner","createCollectingReporter","createFailFastReporter","toStderrOutput","describeUndeployTarget","renderUndeployPlan","undeployLabel","undeployPlanToJson","undeployDebug","runUndeploy","options","adapter","flags","output","json","emitJson","payload","logJson","failurePayload","runOptions","reporter","resolution","resolveTarget","plan","checks","results","reason","type","message","target","some","check","status","failed","find","error","exit","exitCode","result","undeployApp","failure","normalizeFailure","application","undeployed","undefined","log","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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"}
@@ -81,6 +81,12 @@ export class CodemodCommand extends SanityCommand {
81
81
  const dryRun = flags.dry;
82
82
  // We use npx to run jscodeshift, so verify that it exists and works before using it
83
83
  this.ensureNpx();
84
+ const prepared = await mod.prepare?.({
85
+ output: this.output,
86
+ workDir
87
+ });
88
+ // jscodeshift JSON-parses option values, so encode them to keep strings like "2024" as strings
89
+ const transformOptions = Object.entries(prepared?.options ?? {}).map(([key, value])=>`--${key}=${JSON.stringify(value)}`);
84
90
  // If there is a gitignore in the target folder, we want to use that to know
85
91
  // which folders to ignore - eg `dist`, `coverage` or whatever
86
92
  const hasGitIgnore = existsSync(path.join(workDir, '.gitignore'));
@@ -100,7 +106,10 @@ export class CodemodCommand extends SanityCommand {
100
106
  '--extensions',
101
107
  exts.join(','),
102
108
  dryRun && '--dry',
103
- workDir
109
+ ...transformOptions,
110
+ ...prepared?.paths ?? [
111
+ workDir
112
+ ]
104
113
  ].filter((item)=>typeof item === 'string');
105
114
  const child = spawn('npx', cmdArgs, {
106
115
  stdio: 'inherit'
@@ -109,10 +118,12 @@ export class CodemodCommand extends SanityCommand {
109
118
  child.kill(2);
110
119
  };
111
120
  process.on('SIGINT', handleSigint);
112
- child.on('close', (code)=>{
113
- process.removeListener('SIGINT', handleSigint);
114
- this.exit(code || undefined);
121
+ // Wait inside run() so oclif handles the exit, rather than throwing from an event callback
122
+ const code = await new Promise((resolve)=>{
123
+ child.on('close', resolve);
115
124
  });
125
+ process.removeListener('SIGINT', handleSigint);
126
+ this.exit(code || undefined);
116
127
  }
117
128
  ensureNpx() {
118
129
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/codemod.ts"],"sourcesContent":["import {execSync, spawn} from 'node:child_process'\nimport {existsSync} from 'node:fs'\nimport path from 'node:path'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {codemods} from '../actions/codemods/index.js'\nimport {type CodeMod} from '../actions/codemods/types.js'\n\nconst codemodeDebug = subdebug('codemod')\n\nexport class CodemodCommand extends SanityCommand<typeof CodemodCommand> {\n static override args = {\n codemodName: Args.string({\n description: 'Name of the codemod to run',\n required: false,\n }),\n }\n\n static override description = 'Updates Sanity Studio codebase with a code modification script'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Show available code mods',\n },\n {\n command: '<%= config.bin %> <%= command.id %> reactIconsV3 --dry',\n description: 'Run codemod to transform react-icons imports (dry run)',\n },\n ]\n\n static override flags = {\n dry: Flags.boolean({\n default: false,\n description: 'Dry run (no changes are made to files)',\n }),\n extensions: Flags.string({\n default: 'js,ts,tsx',\n description: 'Transform files with these file extensions (comma separated)',\n }),\n 'no-verify': Flags.boolean({\n default: false,\n description: 'Skip verification steps before running codemod',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CodemodCommand)\n const {codemodName} = args\n const workDir = process.cwd()\n\n if (!codemodName) {\n this.printMods()\n return\n }\n\n // Make a record where all the keys of the available codemods are lowercased,\n // eg `reactIconsV3` turns into `{reacticonsv3: CodeMod}`\n const normalizedMods: Map<string, CodeMod> = new Map()\n for (const [originalName, mod] of Object.entries(codemods)) {\n normalizedMods.set(originalName.toLowerCase(), mod)\n }\n\n // Verify that mod exists\n const mod = normalizedMods.get(codemodName.toLowerCase())\n if (!mod) {\n this.error(`Codemod with name \"${codemodName}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n // Verify if there is any verification defined, and user has not opted out of it\n if (typeof mod.verify === 'function' && !flags['no-verify']) {\n try {\n await mod.verify({workDir})\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n codemodeDebug('Verification failed: %s', err.message)\n this.error(`Verification failed: ${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n // If extensions are defined, they come in as CSV, so split and normalize them\n const exts = flags.extensions\n ? flags.extensions.split(',').map((ext) => ext.trim().replace(/^\\./, ''))\n : ['js', 'ts', 'tsx']\n\n const dryRun = flags.dry\n\n // We use npx to run jscodeshift, so verify that it exists and works before using it\n this.ensureNpx()\n\n // If there is a gitignore in the target folder, we want to use that to know\n // which folders to ignore - eg `dist`, `coverage` or whatever\n const hasGitIgnore = existsSync(path.join(workDir, '.gitignore'))\n\n // Build the CLI command arguments\n const cliRoot = path.resolve(import.meta.dirname, '../..')\n const modPath = path.resolve(path.join(cliRoot, 'codemods', mod.filename))\n const cmdArgs = [\n 'jscodeshift',\n '--ignore-pattern',\n 'node_modules',\n '--ignore-pattern',\n 'dist',\n hasGitIgnore && '--ignore-config',\n hasGitIgnore && '.gitignore',\n '-t',\n modPath,\n '--extensions',\n exts.join(','),\n dryRun && '--dry',\n workDir,\n ].filter((item): item is string => typeof item === 'string')\n\n const child = spawn('npx', cmdArgs, {\n stdio: 'inherit',\n })\n\n const handleSigint = () => {\n child.kill(2)\n }\n\n process.on('SIGINT', handleSigint)\n\n child.on('close', (code) => {\n process.removeListener('SIGINT', handleSigint)\n this.exit(code || undefined)\n })\n }\n\n private ensureNpx(): void {\n try {\n const npxHelp = execSync('npx --help', {encoding: 'utf8'})\n if (!npxHelp.includes('npm')) {\n this.error('Not the npx we expected', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch {\n this.error(\n `Failed to run \"npx\" - required to run codemods. Do you have a recent version of npm installed?`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n\n private printMods(): void {\n this.log('Available code modifications:\\n')\n\n for (const [modName, mod] of Object.entries(codemods)) {\n this.log(`${modName} - ${mod.purpose}`)\n }\n }\n}\n"],"names":["execSync","spawn","existsSync","path","Args","Flags","exitCodes","SanityCommand","subdebug","codemods","codemodeDebug","CodemodCommand","args","codemodName","string","description","required","examples","command","flags","dry","boolean","default","extensions","run","parse","workDir","process","cwd","printMods","normalizedMods","Map","originalName","mod","Object","entries","set","toLowerCase","get","error","exit","RUNTIME_ERROR","verify","err","Error","String","message","exts","split","map","ext","trim","replace","dryRun","ensureNpx","hasGitIgnore","join","cliRoot","resolve","dirname","modPath","filename","cmdArgs","filter","item","child","stdio","handleSigint","kill","on","code","removeListener","undefined","npxHelp","encoding","includes","log","modName","purpose"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/commands/codemod.ts"],"sourcesContent":["import {execSync, spawn} from 'node:child_process'\nimport {existsSync} from 'node:fs'\nimport path from 'node:path'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {codemods} from '../actions/codemods/index.js'\nimport {type CodeMod} from '../actions/codemods/types.js'\n\nconst codemodeDebug = subdebug('codemod')\n\nexport class CodemodCommand extends SanityCommand<typeof CodemodCommand> {\n static override args = {\n codemodName: Args.string({\n description: 'Name of the codemod to run',\n required: false,\n }),\n }\n\n static override description = 'Updates Sanity Studio codebase with a code modification script'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Show available code mods',\n },\n {\n command: '<%= config.bin %> <%= command.id %> reactIconsV3 --dry',\n description: 'Run codemod to transform react-icons imports (dry run)',\n },\n ]\n\n static override flags = {\n dry: Flags.boolean({\n default: false,\n description: 'Dry run (no changes are made to files)',\n }),\n extensions: Flags.string({\n default: 'js,ts,tsx',\n description: 'Transform files with these file extensions (comma separated)',\n }),\n 'no-verify': Flags.boolean({\n default: false,\n description: 'Skip verification steps before running codemod',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CodemodCommand)\n const {codemodName} = args\n const workDir = process.cwd()\n\n if (!codemodName) {\n this.printMods()\n return\n }\n\n // Make a record where all the keys of the available codemods are lowercased,\n // eg `reactIconsV3` turns into `{reacticonsv3: CodeMod}`\n const normalizedMods: Map<string, CodeMod> = new Map()\n for (const [originalName, mod] of Object.entries(codemods)) {\n normalizedMods.set(originalName.toLowerCase(), mod)\n }\n\n // Verify that mod exists\n const mod = normalizedMods.get(codemodName.toLowerCase())\n if (!mod) {\n this.error(`Codemod with name \"${codemodName}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n // Verify if there is any verification defined, and user has not opted out of it\n if (typeof mod.verify === 'function' && !flags['no-verify']) {\n try {\n await mod.verify({workDir})\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n codemodeDebug('Verification failed: %s', err.message)\n this.error(`Verification failed: ${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n // If extensions are defined, they come in as CSV, so split and normalize them\n const exts = flags.extensions\n ? flags.extensions.split(',').map((ext) => ext.trim().replace(/^\\./, ''))\n : ['js', 'ts', 'tsx']\n\n const dryRun = flags.dry\n\n // We use npx to run jscodeshift, so verify that it exists and works before using it\n this.ensureNpx()\n\n const prepared = await mod.prepare?.({output: this.output, workDir})\n // jscodeshift JSON-parses option values, so encode them to keep strings like \"2024\" as strings\n const transformOptions = Object.entries(prepared?.options ?? {}).map(\n ([key, value]) => `--${key}=${JSON.stringify(value)}`,\n )\n\n // If there is a gitignore in the target folder, we want to use that to know\n // which folders to ignore - eg `dist`, `coverage` or whatever\n const hasGitIgnore = existsSync(path.join(workDir, '.gitignore'))\n\n // Build the CLI command arguments\n const cliRoot = path.resolve(import.meta.dirname, '../..')\n const modPath = path.resolve(path.join(cliRoot, 'codemods', mod.filename))\n const cmdArgs = [\n 'jscodeshift',\n '--ignore-pattern',\n 'node_modules',\n '--ignore-pattern',\n 'dist',\n hasGitIgnore && '--ignore-config',\n hasGitIgnore && '.gitignore',\n '-t',\n modPath,\n '--extensions',\n exts.join(','),\n dryRun && '--dry',\n ...transformOptions,\n ...(prepared?.paths ?? [workDir]),\n ].filter((item): item is string => typeof item === 'string')\n\n const child = spawn('npx', cmdArgs, {\n stdio: 'inherit',\n })\n\n const handleSigint = () => {\n child.kill(2)\n }\n\n process.on('SIGINT', handleSigint)\n\n // Wait inside run() so oclif handles the exit, rather than throwing from an event callback\n const code = await new Promise<number | null>((resolve) => {\n child.on('close', resolve)\n })\n process.removeListener('SIGINT', handleSigint)\n this.exit(code || undefined)\n }\n\n private ensureNpx(): void {\n try {\n const npxHelp = execSync('npx --help', {encoding: 'utf8'})\n if (!npxHelp.includes('npm')) {\n this.error('Not the npx we expected', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch {\n this.error(\n `Failed to run \"npx\" - required to run codemods. Do you have a recent version of npm installed?`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n\n private printMods(): void {\n this.log('Available code modifications:\\n')\n\n for (const [modName, mod] of Object.entries(codemods)) {\n this.log(`${modName} - ${mod.purpose}`)\n }\n }\n}\n"],"names":["execSync","spawn","existsSync","path","Args","Flags","exitCodes","SanityCommand","subdebug","codemods","codemodeDebug","CodemodCommand","args","codemodName","string","description","required","examples","command","flags","dry","boolean","default","extensions","run","parse","workDir","process","cwd","printMods","normalizedMods","Map","originalName","mod","Object","entries","set","toLowerCase","get","error","exit","RUNTIME_ERROR","verify","err","Error","String","message","exts","split","map","ext","trim","replace","dryRun","ensureNpx","prepared","prepare","output","transformOptions","options","key","value","JSON","stringify","hasGitIgnore","join","cliRoot","resolve","dirname","modPath","filename","cmdArgs","paths","filter","item","child","stdio","handleSigint","kill","on","code","Promise","removeListener","undefined","npxHelp","encoding","includes","log","modName","purpose"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Args, Flags } from '@oclif/core';
1
+ import { Args } from '@oclif/core';
2
2
  import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
3
  import { getErrorMessage } from '@sanity/cli-core/errors';
4
4
  import { isHttpError } from '@sanity/client';
@@ -13,6 +13,7 @@ export class GetKnowledgeBaseCommand extends SanityCommand {
13
13
  })
14
14
  };
15
15
  static description = 'Get details of a knowledge base';
16
+ static enableJsonFlag = true;
16
17
  static examples = [
17
18
  {
18
19
  command: '<%= config.bin %> <%= command.id %> kb-abc123',
@@ -23,12 +24,6 @@ export class GetKnowledgeBaseCommand extends SanityCommand {
23
24
  description: 'Output the knowledge base as JSON'
24
25
  }
25
26
  ];
26
- static flags = {
27
- json: Flags.boolean({
28
- default: false,
29
- description: 'Output the knowledge base in JSON format'
30
- })
31
- };
32
27
  async run() {
33
28
  const { knowledgeBaseId } = this.args;
34
29
  const { json } = this.flags;
@@ -47,7 +42,7 @@ export class GetKnowledgeBaseCommand extends SanityCommand {
47
42
  });
48
43
  }
49
44
  if (json) {
50
- this.log(JSON.stringify(knowledgeBase, null, 2));
45
+ this.logJson(knowledgeBase);
51
46
  return;
52
47
  }
53
48
  const pending = knowledgeBase.pendingChanges;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/context/get.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {formatKeyValue} from '../../actions/debug/output.js'\nimport {getKnowledgeBase} from '../../services/context.js'\n\nconst getContextDebug = subdebug('context:get')\n\nexport class GetKnowledgeBaseCommand extends SanityCommand<typeof GetKnowledgeBaseCommand> {\n static override args = {\n knowledgeBaseId: Args.string({\n description: 'Knowledge base ID',\n required: true,\n }),\n }\n\n static override description = 'Get details of a knowledge base'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123',\n description: 'Get details of a specific knowledge base',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --json',\n description: 'Output the knowledge base as JSON',\n },\n ]\n\n static override flags = {\n json: Flags.boolean({\n default: false,\n description: 'Output the knowledge base in JSON format',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {knowledgeBaseId} = this.args\n const {json} = this.flags\n\n let knowledgeBase\n try {\n knowledgeBase = await getKnowledgeBase(knowledgeBaseId)\n } catch (error) {\n getContextDebug('Error getting knowledge base', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Knowledge base \"${knowledgeBaseId}\" not found`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to get knowledge base: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (json) {\n this.log(JSON.stringify(knowledgeBase, null, 2))\n return\n }\n\n const pending = knowledgeBase.pendingChanges\n const padTo = 15 // \"Pending changes\" is the longest key\n this.log(formatKeyValue('ID', knowledgeBase.publicId, {padTo}))\n this.log(formatKeyValue('Title', knowledgeBase.title, {padTo}))\n this.log(formatKeyValue('Description', knowledgeBase.description, {padTo}))\n this.log(formatKeyValue('Organization', knowledgeBase.organizationId, {padTo}))\n this.log(formatKeyValue('State', knowledgeBase.state, {padTo}))\n this.log(formatKeyValue('Active job', knowledgeBase.activeJobId ?? '-', {padTo}))\n this.log(\n formatKeyValue(\n 'Pending changes',\n pending\n ? `${pending.added} added, ${pending.changed} changed, ${pending.removed} removed`\n : '-',\n {padTo},\n ),\n )\n this.log(\n formatKeyValue(\n 'Refresh',\n knowledgeBase.refreshEnabled ? `enabled (${knowledgeBase.refreshFrequency})` : 'disabled',\n {padTo},\n ),\n )\n this.log(formatKeyValue('Open issues', knowledgeBase.openIssueCount, {padTo}))\n this.log(formatKeyValue('Instructions', knowledgeBase.instructionCount, {padTo}))\n this.log(formatKeyValue('Created', knowledgeBase.createdAt, {padTo}))\n this.log(formatKeyValue('Updated', knowledgeBase.updatedAt, {padTo}))\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","formatKeyValue","getKnowledgeBase","getContextDebug","GetKnowledgeBaseCommand","args","knowledgeBaseId","string","description","required","examples","command","flags","json","boolean","default","run","knowledgeBase","error","statusCode","exit","RUNTIME_ERROR","log","JSON","stringify","pending","pendingChanges","padTo","publicId","title","organizationId","state","activeJobId","added","changed","removed","refreshEnabled","refreshFrequency","openIssueCount","instructionCount","createdAt","updatedAt"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/context/get.ts"],"sourcesContent":["import {Args} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {formatKeyValue} from '../../actions/debug/output.js'\nimport {getKnowledgeBase} from '../../services/context.js'\n\nconst getContextDebug = subdebug('context:get')\n\nexport class GetKnowledgeBaseCommand extends SanityCommand<typeof GetKnowledgeBaseCommand> {\n static override args = {\n knowledgeBaseId: Args.string({\n description: 'Knowledge base ID',\n required: true,\n }),\n }\n\n static override description = 'Get details of a knowledge base'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123',\n description: 'Get details of a specific knowledge base',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --json',\n description: 'Output the knowledge base as JSON',\n },\n ]\n\n public async run(): Promise<void> {\n const {knowledgeBaseId} = this.args\n const {json} = this.flags\n\n let knowledgeBase\n try {\n knowledgeBase = await getKnowledgeBase(knowledgeBaseId)\n } catch (error) {\n getContextDebug('Error getting knowledge base', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Knowledge base \"${knowledgeBaseId}\" not found`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to get knowledge base: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (json) {\n this.logJson(knowledgeBase)\n return\n }\n\n const pending = knowledgeBase.pendingChanges\n const padTo = 15 // \"Pending changes\" is the longest key\n this.log(formatKeyValue('ID', knowledgeBase.publicId, {padTo}))\n this.log(formatKeyValue('Title', knowledgeBase.title, {padTo}))\n this.log(formatKeyValue('Description', knowledgeBase.description, {padTo}))\n this.log(formatKeyValue('Organization', knowledgeBase.organizationId, {padTo}))\n this.log(formatKeyValue('State', knowledgeBase.state, {padTo}))\n this.log(formatKeyValue('Active job', knowledgeBase.activeJobId ?? '-', {padTo}))\n this.log(\n formatKeyValue(\n 'Pending changes',\n pending\n ? `${pending.added} added, ${pending.changed} changed, ${pending.removed} removed`\n : '-',\n {padTo},\n ),\n )\n this.log(\n formatKeyValue(\n 'Refresh',\n knowledgeBase.refreshEnabled ? `enabled (${knowledgeBase.refreshFrequency})` : 'disabled',\n {padTo},\n ),\n )\n this.log(formatKeyValue('Open issues', knowledgeBase.openIssueCount, {padTo}))\n this.log(formatKeyValue('Instructions', knowledgeBase.instructionCount, {padTo}))\n this.log(formatKeyValue('Created', knowledgeBase.createdAt, {padTo}))\n this.log(formatKeyValue('Updated', knowledgeBase.updatedAt, {padTo}))\n }\n}\n"],"names":["Args","exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","formatKeyValue","getKnowledgeBase","getContextDebug","GetKnowledgeBaseCommand","args","knowledgeBaseId","string","description","required","enableJsonFlag","examples","command","run","json","flags","knowledgeBase","error","statusCode","exit","RUNTIME_ERROR","logJson","pending","pendingChanges","padTo","log","publicId","title","organizationId","state","activeJobId","added","changed","removed","refreshEnabled","refreshFrequency","openIssueCount","instructionCount","createdAt","updatedAt"],"mappings":"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"}