@sanity/cli 6.6.0 → 6.7.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 (183) hide show
  1. package/README.md +12 -4
  2. package/dist/actions/auth/login/login.js +4 -1
  3. package/dist/actions/auth/login/login.js.map +1 -1
  4. package/dist/actions/build/buildApp.js +4 -5
  5. package/dist/actions/build/buildApp.js.map +1 -1
  6. package/dist/actions/build/buildStaticFiles.js +12 -4
  7. package/dist/actions/build/buildStaticFiles.js.map +1 -1
  8. package/dist/actions/build/buildStudio.js +6 -14
  9. package/dist/actions/build/buildStudio.js.map +1 -1
  10. package/dist/actions/build/{getStudioEnvironmentVariables.js → getEnvironmentVariables.js} +15 -19
  11. package/dist/actions/build/getEnvironmentVariables.js.map +1 -0
  12. package/dist/actions/deploy/deployStudio.js +1 -2
  13. package/dist/actions/deploy/deployStudio.js.map +1 -1
  14. package/dist/actions/deploy/deployStudioSchemasAndManifests.js +1 -2
  15. package/dist/actions/deploy/deployStudioSchemasAndManifests.js.map +1 -1
  16. package/dist/actions/deploy/deployStudioSchemasAndManifests.worker.js +1 -1
  17. package/dist/actions/deploy/deployStudioSchemasAndManifests.worker.js.map +1 -1
  18. package/dist/actions/dev/startStudioDevServer.js +1 -1
  19. package/dist/actions/dev/startStudioDevServer.js.map +1 -1
  20. package/dist/actions/documents/validateDocuments.worker.js +1 -2
  21. package/dist/actions/documents/validateDocuments.worker.js.map +1 -1
  22. package/dist/actions/graphql/SchemaError.js +1 -1
  23. package/dist/actions/graphql/SchemaError.js.map +1 -1
  24. package/dist/actions/init/initAction.js +32 -1
  25. package/dist/actions/init/initAction.js.map +1 -1
  26. package/dist/actions/init/scaffoldTemplate.js +32 -18
  27. package/dist/actions/init/scaffoldTemplate.js.map +1 -1
  28. package/dist/actions/init/templates/index.js +2 -0
  29. package/dist/actions/init/templates/index.js.map +1 -1
  30. package/dist/actions/init/templates/pageBuilder.js +32 -0
  31. package/dist/actions/init/templates/pageBuilder.js.map +1 -0
  32. package/dist/actions/init/types.js +2 -1
  33. package/dist/actions/init/types.js.map +1 -1
  34. package/dist/actions/manifest/extractManifest.js +1 -1
  35. package/dist/actions/manifest/extractManifest.js.map +1 -1
  36. package/dist/actions/manifest/extractManifest.worker.js +1 -1
  37. package/dist/actions/manifest/extractManifest.worker.js.map +1 -1
  38. package/dist/actions/mcp/editorConfigs.js +51 -11
  39. package/dist/actions/mcp/editorConfigs.js.map +1 -1
  40. package/dist/actions/mcp/promptForMCPSetup.js +16 -11
  41. package/dist/actions/mcp/promptForMCPSetup.js.map +1 -1
  42. package/dist/actions/mcp/setupMCP.js +180 -61
  43. package/dist/actions/mcp/setupMCP.js.map +1 -1
  44. package/dist/actions/mcp/types.js.map +1 -1
  45. package/dist/actions/schema/deploySchemas.js +1 -1
  46. package/dist/actions/schema/deploySchemas.js.map +1 -1
  47. package/dist/actions/schema/extractSanityWorkspace.worker.js +1 -1
  48. package/dist/actions/schema/extractSanityWorkspace.worker.js.map +1 -1
  49. package/dist/actions/schema/extractSchema.js +1 -4
  50. package/dist/actions/schema/extractSchema.js.map +1 -1
  51. package/dist/actions/schema/extractSchemaWatcher.js +1 -4
  52. package/dist/actions/schema/extractSchemaWatcher.js.map +1 -1
  53. package/dist/actions/schema/getExtractOptions.js +8 -18
  54. package/dist/actions/schema/getExtractOptions.js.map +1 -1
  55. package/dist/actions/schema/types.js +0 -6
  56. package/dist/actions/schema/types.js.map +1 -1
  57. package/dist/actions/schema/validateAction.js +1 -1
  58. package/dist/actions/schema/validateAction.js.map +1 -1
  59. package/dist/actions/schema/validateSchema.worker.js +1 -2
  60. package/dist/actions/schema/validateSchema.worker.js.map +1 -1
  61. package/dist/actions/schema/watchExtractSchema.js +1 -1
  62. package/dist/actions/schema/watchExtractSchema.js.map +1 -1
  63. package/dist/actions/skills/readSkillState.js +54 -0
  64. package/dist/actions/skills/readSkillState.js.map +1 -0
  65. package/dist/actions/skills/setupSkills.js +73 -0
  66. package/dist/actions/skills/setupSkills.js.map +1 -0
  67. package/dist/commands/build.js +9 -22
  68. package/dist/commands/build.js.map +1 -1
  69. package/dist/commands/cors/add.js +5 -5
  70. package/dist/commands/cors/add.js.map +1 -1
  71. package/dist/commands/datasets/export.js +9 -0
  72. package/dist/commands/datasets/export.js.map +1 -1
  73. package/dist/commands/docs/read.js +33 -12
  74. package/dist/commands/docs/read.js.map +1 -1
  75. package/dist/commands/init.js +12 -1
  76. package/dist/commands/init.js.map +1 -1
  77. package/dist/commands/manifest/extract.js +1 -2
  78. package/dist/commands/manifest/extract.js.map +1 -1
  79. package/dist/commands/mcp/configure.js +2 -1
  80. package/dist/commands/mcp/configure.js.map +1 -1
  81. package/dist/commands/schemas/deploy.js +1 -2
  82. package/dist/commands/schemas/deploy.js.map +1 -1
  83. package/dist/exports/_internal.d.ts +2 -1
  84. package/dist/exports/_internal.js +1 -1
  85. package/dist/exports/_internal.js.map +1 -1
  86. package/dist/server/devServer.js +25 -4
  87. package/dist/server/devServer.js.map +1 -1
  88. package/dist/server/previewServer.js +1 -1
  89. package/dist/server/previewServer.js.map +1 -1
  90. package/dist/services/mcp.js +1 -1
  91. package/dist/services/mcp.js.map +1 -1
  92. package/dist/telemetry/init.telemetry.js.map +1 -1
  93. package/oclif.manifest.json +134 -118
  94. package/package.json +15 -16
  95. package/templates/page-builder/README.md +9 -0
  96. package/templates/page-builder/schemaTypes/hero.js +31 -0
  97. package/templates/page-builder/schemaTypes/index.js +19 -0
  98. package/templates/page-builder/static/.gitkeep +0 -0
  99. package/dist/actions/build/buildDebug.js +0 -4
  100. package/dist/actions/build/buildDebug.js.map +0 -1
  101. package/dist/actions/build/buildVendorDependencies.js +0 -149
  102. package/dist/actions/build/buildVendorDependencies.js.map +0 -1
  103. package/dist/actions/build/checkStudioDependencyVersions.js +0 -155
  104. package/dist/actions/build/checkStudioDependencyVersions.js.map +0 -1
  105. package/dist/actions/build/createExternalFromImportMap.js +0 -11
  106. package/dist/actions/build/createExternalFromImportMap.js.map +0 -1
  107. package/dist/actions/build/decorateIndexWithAutoGeneratedWarning.js +0 -13
  108. package/dist/actions/build/decorateIndexWithAutoGeneratedWarning.js.map +0 -1
  109. package/dist/actions/build/decorateIndexWithBridgeScript.js +0 -17
  110. package/dist/actions/build/decorateIndexWithBridgeScript.js.map +0 -1
  111. package/dist/actions/build/decorateIndexWithStagingScript.js +0 -16
  112. package/dist/actions/build/decorateIndexWithStagingScript.js.map +0 -1
  113. package/dist/actions/build/getAppEnvVars.js +0 -9
  114. package/dist/actions/build/getAppEnvVars.js.map +0 -1
  115. package/dist/actions/build/getEntryModule.js +0 -46
  116. package/dist/actions/build/getEntryModule.js.map +0 -1
  117. package/dist/actions/build/getPossibleDocumentComponentLocations.js +0 -11
  118. package/dist/actions/build/getPossibleDocumentComponentLocations.js.map +0 -1
  119. package/dist/actions/build/getStudioEnvVars.js +0 -9
  120. package/dist/actions/build/getStudioEnvVars.js.map +0 -1
  121. package/dist/actions/build/getStudioEnvironmentVariables.js.map +0 -1
  122. package/dist/actions/build/getViteConfig.js +0 -219
  123. package/dist/actions/build/getViteConfig.js.map +0 -1
  124. package/dist/actions/build/normalizeBasePath.js +0 -9
  125. package/dist/actions/build/normalizeBasePath.js.map +0 -1
  126. package/dist/actions/build/renderDocument.js +0 -50
  127. package/dist/actions/build/renderDocument.js.map +0 -1
  128. package/dist/actions/build/renderDocument.worker.js +0 -9
  129. package/dist/actions/build/renderDocument.worker.js.map +0 -1
  130. package/dist/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.js +0 -79
  131. package/dist/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.js.map +0 -1
  132. package/dist/actions/build/renderDocumentWorker/components/BasicDocument.js +0 -61
  133. package/dist/actions/build/renderDocumentWorker/components/BasicDocument.js.map +0 -1
  134. package/dist/actions/build/renderDocumentWorker/components/DefaultDocument.js +0 -165
  135. package/dist/actions/build/renderDocumentWorker/components/DefaultDocument.js.map +0 -1
  136. package/dist/actions/build/renderDocumentWorker/components/Favicons.js +0 -28
  137. package/dist/actions/build/renderDocumentWorker/components/Favicons.js.map +0 -1
  138. package/dist/actions/build/renderDocumentWorker/components/GlobalErrorHandler.js +0 -178
  139. package/dist/actions/build/renderDocumentWorker/components/GlobalErrorHandler.js.map +0 -1
  140. package/dist/actions/build/renderDocumentWorker/components/NoJavascript.js +0 -51
  141. package/dist/actions/build/renderDocumentWorker/components/NoJavascript.js.map +0 -1
  142. package/dist/actions/build/renderDocumentWorker/getDocumentComponent.js +0 -41
  143. package/dist/actions/build/renderDocumentWorker/getDocumentComponent.js.map +0 -1
  144. package/dist/actions/build/renderDocumentWorker/getDocumentHtml.js +0 -55
  145. package/dist/actions/build/renderDocumentWorker/getDocumentHtml.js.map +0 -1
  146. package/dist/actions/build/renderDocumentWorker/renderDocumentWorker.js +0 -31
  147. package/dist/actions/build/renderDocumentWorker/renderDocumentWorker.js.map +0 -1
  148. package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js +0 -30
  149. package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js.map +0 -1
  150. package/dist/actions/build/renderDocumentWorker/types.js +0 -5
  151. package/dist/actions/build/renderDocumentWorker/types.js.map +0 -1
  152. package/dist/actions/build/writeSanityRuntime.js +0 -64
  153. package/dist/actions/build/writeSanityRuntime.js.map +0 -1
  154. package/dist/actions/docs/normalizeDocsPath.js +0 -15
  155. package/dist/actions/docs/normalizeDocsPath.js.map +0 -1
  156. package/dist/actions/schema/extractSanitySchema.worker.js +0 -33
  157. package/dist/actions/schema/extractSanitySchema.worker.js.map +0 -1
  158. package/dist/actions/schema/formatSchemaValidation.js +0 -78
  159. package/dist/actions/schema/formatSchemaValidation.js.map +0 -1
  160. package/dist/actions/schema/matchSchemaPattern.js +0 -22
  161. package/dist/actions/schema/matchSchemaPattern.js.map +0 -1
  162. package/dist/actions/schema/runSchemaExtraction.js +0 -39
  163. package/dist/actions/schema/runSchemaExtraction.js.map +0 -1
  164. package/dist/actions/schema/utils/SchemaExtractionError.js +0 -10
  165. package/dist/actions/schema/utils/SchemaExtractionError.js.map +0 -1
  166. package/dist/actions/schema/utils/extractValidationFromSchemaError.js +0 -12
  167. package/dist/actions/schema/utils/extractValidationFromSchemaError.js.map +0 -1
  168. package/dist/constants.js +0 -8
  169. package/dist/constants.js.map +0 -1
  170. package/dist/server/vite/plugin-sanity-build-entries.js +0 -67
  171. package/dist/server/vite/plugin-sanity-build-entries.js.map +0 -1
  172. package/dist/server/vite/plugin-sanity-favicons.js +0 -72
  173. package/dist/server/vite/plugin-sanity-favicons.js.map +0 -1
  174. package/dist/server/vite/plugin-sanity-runtime-rewrite.js +0 -18
  175. package/dist/server/vite/plugin-sanity-runtime-rewrite.js.map +0 -1
  176. package/dist/server/vite/plugin-schema-extraction.js +0 -201
  177. package/dist/server/vite/plugin-schema-extraction.js.map +0 -1
  178. package/dist/telemetry/build.telemetry.js +0 -13
  179. package/dist/telemetry/build.telemetry.js.map +0 -1
  180. package/dist/telemetry/extractSchema.telemetry.js +0 -18
  181. package/dist/telemetry/extractSchema.telemetry.js.map +0 -1
  182. package/dist/util/getWorkspace.js +0 -18
  183. package/dist/util/getWorkspace.js.map +0 -1
@@ -2,30 +2,116 @@ import { ux } from '@oclif/core';
2
2
  import { subdebug } from '@sanity/cli-core';
3
3
  import { logSymbols } from '@sanity/cli-core/ux';
4
4
  import { createMCPToken, MCP_SERVER_URL } from '../../services/mcp.js';
5
+ import { readSkillState } from '../skills/readSkillState.js';
6
+ import { SANITY_SKILL_NAME } from '../skills/setupSkills.js';
5
7
  import { detectAvailableEditors } from './detectAvailableEditors.js';
6
- import { EDITOR_CONFIGS } from './editorConfigs.js';
8
+ import { EDITOR_CONFIGS, getSkillsCliAgent, getSkillsCliAgentDisplayName } from './editorConfigs.js';
7
9
  import { promptForMCPSetup } from './promptForMCPSetup.js';
8
10
  import { validateEditorTokens } from './validateEditorTokens.js';
9
11
  import { writeMCPConfig } from './writeMCPConfig.js';
10
12
  const mcpDebug = subdebug('mcp:setup');
11
13
  const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`;
12
14
  /**
13
- * Main MCP setup orchestration
14
- * Opt-out by default: runs automatically unless skip option is set
15
+ * Classify each editor into one of four actions based on MCP status and
16
+ * whether a Sanity skill is already installed for its skills-CLI agent.
17
+ */ function classifyEditors(editors) {
18
+ return editors.map((editor)=>{
19
+ const needsMCP = !editor.configured || editor.authStatus !== 'valid';
20
+ const skillsCliAgent = getSkillsCliAgent(editor.name);
21
+ const hasSkillMapping = Boolean(skillsCliAgent);
22
+ const skillInstalled = editor.skillInstalled === true;
23
+ if (needsMCP) {
24
+ return {
25
+ action: hasSkillMapping ? 'mcp-and-skill' : 'mcp-only',
26
+ editor
27
+ };
28
+ }
29
+ if (hasSkillMapping && !skillInstalled) {
30
+ return {
31
+ action: 'skill-only',
32
+ editor
33
+ };
34
+ }
35
+ return {
36
+ action: 'none',
37
+ editor
38
+ };
39
+ });
40
+ }
41
+ /**
42
+ * Apply masking based on the configured modes. `skip` modes mute the
43
+ * corresponding action so we never prompt for or run work the user opted out
44
+ * of via `--no-mcp` / `--no-skills`.
45
+ */ function applyMasking(classified, mcpMode, skillsMode) {
46
+ const actionable = [];
47
+ for (const { action, editor } of classified){
48
+ if (action === 'none') continue;
49
+ if (mcpMode === 'skip' && skillsMode === 'skip') continue;
50
+ if (mcpMode === 'skip') {
51
+ // No MCP writes — keep only skill-only / mcp-and-skill (downgraded to skill-only)
52
+ if (action === 'mcp-only') continue;
53
+ if (action === 'mcp-and-skill') {
54
+ actionable.push({
55
+ action: 'skill-only',
56
+ editor
57
+ });
58
+ continue;
59
+ }
60
+ actionable.push({
61
+ action,
62
+ editor
63
+ });
64
+ continue;
65
+ }
66
+ if (skillsMode === 'skip') {
67
+ // No skill install — drop skill-only, downgrade mcp-and-skill → mcp-only
68
+ if (action === 'skill-only') continue;
69
+ if (action === 'mcp-and-skill') {
70
+ actionable.push({
71
+ action: 'mcp-only',
72
+ editor
73
+ });
74
+ continue;
75
+ }
76
+ actionable.push({
77
+ action,
78
+ editor
79
+ });
80
+ continue;
81
+ }
82
+ actionable.push({
83
+ action,
84
+ editor
85
+ });
86
+ }
87
+ return actionable;
88
+ }
89
+ function getPromptMessage(mcpMode, skillsMode) {
90
+ if (mcpMode === 'skip') return 'Install Sanity agent skills for these editors?';
91
+ if (skillsMode === 'skip') return 'Configure Sanity MCP server?';
92
+ return 'Configure Sanity MCP and install agent skills for these editors?';
93
+ }
94
+ /**
95
+ * Main MCP setup orchestration.
96
+ *
97
+ * When `skillsMode !== 'skip'`, the prompt combines MCP and skill offers,
98
+ * and the result includes `skillsToInstall` — agent IDs the caller should
99
+ * install via `setupSkills`. `setupMCP` itself never installs skills.
15
100
  */ export async function setupMCP(options) {
16
- const { explicit = false, mode = 'prompt' } = options ?? {};
17
- // 1. Check for explicit opt-out
18
- if (mode === 'skip') {
19
- mcpDebug('Skipping MCP configuration (mode: skip)');
101
+ const { explicit = false, mode: mcpMode = 'prompt', skillsMode = 'skip' } = options ?? {};
102
+ // 1. Both opted out → nothing to do.
103
+ if (mcpMode === 'skip' && skillsMode === 'skip') {
104
+ mcpDebug('Skipping setup (mcpMode: skip, skillsMode: skip)');
20
105
  return {
21
106
  alreadyConfiguredEditors: [],
22
107
  configuredEditors: [],
23
108
  detectedEditors: [],
109
+ skillsToInstall: [],
24
110
  skipped: true
25
111
  };
26
112
  }
27
113
  // 2. Detect available editors (filters out unparseable configs)
28
- const editors = await detectAvailableEditors();
114
+ const editors = options?.editors ?? await detectAvailableEditors();
29
115
  const detectedEditors = editors.map((e)=>e.name);
30
116
  mcpDebug('Detected %d editors: %s', detectedEditors.length, detectedEditors);
31
117
  if (editors.length === 0) {
@@ -36,16 +122,31 @@ const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server f
36
122
  alreadyConfiguredEditors: [],
37
123
  configuredEditors: [],
38
124
  detectedEditors,
125
+ skillsToInstall: [],
39
126
  skipped: true
40
127
  };
41
128
  }
42
129
  // 3. Validate existing tokens against the Sanity API
43
130
  await validateEditorTokens(editors);
44
- // 4. Check if there's anything actionable
45
- const actionable = editors.filter((e)=>!e.configured || e.authStatus !== 'valid');
131
+ // 4. Read skill state when skills are in scope so classification can dedup
132
+ if (skillsMode !== 'skip') {
133
+ const { installedAgentDisplayNames } = await readSkillState({
134
+ skillName: SANITY_SKILL_NAME
135
+ });
136
+ for (const editor of editors){
137
+ const displayName = getSkillsCliAgentDisplayName(editor.name);
138
+ editor.skillInstalled = displayName ? installedAgentDisplayNames.has(displayName) : false;
139
+ }
140
+ }
141
+ // 5. Classify + mask
142
+ const classified = classifyEditors(editors);
143
+ const actionable = applyMasking(classified, mcpMode, skillsMode);
144
+ // "Already configured" surfaces editors whose MCP setup is valid (skill
145
+ // state doesn't matter for this signal — that's what skillsToInstall is for).
146
+ const actionableNames = new Set(actionable.map((c)=>c.editor.name));
147
+ const alreadyConfiguredEditors = editors.filter((e)=>e.configured && e.authStatus === 'valid' && !actionableNames.has(e.name)).map((e)=>e.name);
46
148
  if (actionable.length === 0) {
47
- mcpDebug('All editors configured with valid credentials');
48
- const alreadyConfiguredEditors = editors.filter((e)=>e.configured && e.authStatus === 'valid').map((e)=>e.name);
149
+ mcpDebug('Nothing actionable after classification + masking');
49
150
  if (explicit) {
50
151
  ux.stdout(`${logSymbols.success} All detected editors are already configured`);
51
152
  }
@@ -53,76 +154,94 @@ const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server f
53
154
  alreadyConfiguredEditors,
54
155
  configuredEditors: [],
55
156
  detectedEditors,
157
+ skillsToInstall: [],
56
158
  skipped: true
57
159
  };
58
160
  }
59
- // Non-actionable editors are already configured with valid credentials
60
- const alreadyConfiguredEditors = editors.filter((e)=>!actionable.includes(e)).map((e)=>e.name);
61
- // 5. Select editors to configure prompt interactively or auto-select all
62
- const selected = mode === 'auto' ? actionable : await promptForMCPSetup(actionable);
161
+ // 6. Select editors to configure prompt interactively or auto-select all.
162
+ // We only auto when neither side wants a prompt: MCP auto, or (MCP skip
163
+ // + skills auto). Anything that asks `mode: 'prompt'` for MCP wins the
164
+ // prompt even when skills would have auto-installed.
165
+ const shouldAuto = mcpMode === 'auto' || mcpMode === 'skip' && skillsMode === 'auto';
166
+ const selected = shouldAuto ? actionable : await promptForMCPSetup({
167
+ choices: actionable,
168
+ message: getPromptMessage(mcpMode, skillsMode)
169
+ });
63
170
  if (!selected || selected.length === 0) {
64
- // User deselected all editors
65
171
  ux.stdout('MCP configuration skipped');
66
172
  return {
67
173
  alreadyConfiguredEditors,
68
174
  configuredEditors: [],
69
175
  detectedEditors,
176
+ skillsToInstall: [],
70
177
  skipped: true
71
178
  };
72
179
  }
73
- // 6. Get a tokenreuse a valid existing one or create a new one
180
+ // 7. MCP write phaseonly for choices that need MCP
181
+ const mcpSelected = selected.filter((c)=>c.action === 'mcp-only' || c.action === 'mcp-and-skill');
74
182
  let token;
75
- // Look for an existing valid token we can reuse
76
- const validEditor = editors.find((e)=>e.authStatus === 'valid' && e.existingToken);
77
- if (validEditor?.existingToken) {
78
- mcpDebug('Reusing valid token from %s', validEditor.name);
79
- token = validEditor.existingToken;
80
- }
81
- const allOAuth = selected.every((e)=>EDITOR_CONFIGS[e.name].oauthOnly);
82
- // Fall back to creating a new token
83
- // If all editors use OAuth, we don't need to create a token
84
- if (!token && !allOAuth) {
85
- try {
86
- token = await createMCPToken();
87
- } catch (error) {
88
- const err = error instanceof Error ? error : new Error(String(error));
89
- mcpDebug('Error creating MCP token', error);
90
- ux.warn(`Could not configure MCP: ${err.message}`);
91
- ux.warn('You can set up MCP manually later using https://mcp.sanity.io');
92
- return {
93
- alreadyConfiguredEditors,
94
- configuredEditors: [],
95
- detectedEditors,
96
- error: err,
97
- skipped: false
98
- };
183
+ const configuredEditors = [];
184
+ let mcpError;
185
+ if (mcpSelected.length > 0) {
186
+ const validEditor = editors.find((e)=>e.authStatus === 'valid' && e.existingToken);
187
+ if (validEditor?.existingToken) {
188
+ mcpDebug('Reusing valid token from %s', validEditor.name);
189
+ token = validEditor.existingToken;
190
+ }
191
+ const allOAuth = mcpSelected.every((c)=>EDITOR_CONFIGS[c.editor.name].oauthOnly);
192
+ if (!token && !allOAuth) {
193
+ try {
194
+ token = await createMCPToken();
195
+ } catch (error) {
196
+ const err = error instanceof Error ? error : new Error(String(error));
197
+ mcpDebug('Error creating MCP token', error);
198
+ ux.warn(`Could not configure MCP: ${err.message}`);
199
+ ux.warn('You can set up MCP manually later using https://mcp.sanity.io');
200
+ mcpError = err;
201
+ }
202
+ }
203
+ if (!mcpError) {
204
+ for (const choice of mcpSelected){
205
+ try {
206
+ await writeMCPConfig(choice.editor, token);
207
+ configuredEditors.push(choice.editor.name);
208
+ } catch (error) {
209
+ const err = error instanceof Error ? error : new Error(String(error));
210
+ mcpDebug('Error writing MCP config for %s: %O', choice.editor.name, error);
211
+ ux.warn(`Could not configure MCP for ${choice.editor.name}: ${err.message}`);
212
+ ux.warn('You can set up MCP manually later using https://mcp.sanity.io');
213
+ mcpError = err;
214
+ }
215
+ }
216
+ }
217
+ if (configuredEditors.length > 0) {
218
+ ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`);
99
219
  }
100
220
  }
101
- // 7. Write configs for each selected editor
102
- const configuredEditors = [];
103
- try {
104
- for (const editor of selected){
105
- await writeMCPConfig(editor, token);
106
- configuredEditors.push(editor.name);
221
+ // 8. Build skillsToInstall — only for choices the user kept, only when the
222
+ // associated MCP write succeeded (or wasn't needed).
223
+ const skillsToInstall = [];
224
+ if (skillsMode !== 'skip') {
225
+ for (const choice of selected){
226
+ if (choice.action === 'skill-only') {
227
+ const agent = getSkillsCliAgent(choice.editor.name);
228
+ if (agent) skillsToInstall.push(agent);
229
+ continue;
230
+ }
231
+ if (choice.action === 'mcp-and-skill' && configuredEditors.includes(choice.editor.name)) {
232
+ const agent = getSkillsCliAgent(choice.editor.name);
233
+ if (agent) skillsToInstall.push(agent);
234
+ }
107
235
  }
108
- } catch (error) {
109
- const err = error instanceof Error ? error : new Error(String(error));
110
- mcpDebug('Error writing MCP config', error);
111
- ux.warn(`Could not configure MCP: ${err.message}`);
112
- ux.warn('You can set up MCP manually later using https://mcp.sanity.io');
113
- return {
114
- alreadyConfiguredEditors,
115
- configuredEditors,
116
- detectedEditors,
117
- error: err,
118
- skipped: false
119
- };
120
236
  }
121
- ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`);
122
237
  return {
123
238
  alreadyConfiguredEditors,
124
239
  configuredEditors,
125
240
  detectedEditors,
241
+ error: mcpError,
242
+ skillsToInstall: [
243
+ ...new Set(skillsToInstall)
244
+ ],
126
245
  skipped: false
127
246
  };
128
247
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/mcp/setupMCP.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {createMCPToken, MCP_SERVER_URL} from '../../services/mcp.js'\nimport {detectAvailableEditors} from './detectAvailableEditors.js'\nimport {EDITOR_CONFIGS, type EditorName} from './editorConfigs.js'\nimport {promptForMCPSetup} from './promptForMCPSetup.js'\nimport {validateEditorTokens} from './validateEditorTokens.js'\nimport {writeMCPConfig} from './writeMCPConfig.js'\n\nconst mcpDebug = subdebug('mcp:setup')\n\nconst NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`\n\ninterface MCPSetupOptions {\n /**\n * Whether the user explicitly requested MCP configuration (e.g. `sanity mcp configure`).\n * When true, shows status messages even when there's nothing to do.\n * When false/undefined (e.g. called from `sanity init`), stays quiet.\n */\n explicit?: boolean\n\n /**\n * Controls how MCP setup behaves:\n * - 'prompt': Ask the user which editors to configure (default)\n * - 'auto': Auto-configure all detected editors without prompting\n * - 'skip': Skip MCP configuration entirely\n */\n mode?: 'auto' | 'prompt' | 'skip'\n}\n\ninterface MCPSetupResult {\n /** Editors that were already configured with valid credentials (nothing to do) */\n alreadyConfiguredEditors: EditorName[]\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n skipped: boolean\n\n error?: Error\n}\n\n/**\n * Main MCP setup orchestration\n * Opt-out by default: runs automatically unless skip option is set\n */\nexport async function setupMCP(options?: MCPSetupOptions): Promise<MCPSetupResult> {\n const {explicit = false, mode = 'prompt'} = options ?? {}\n\n // 1. Check for explicit opt-out\n if (mode === 'skip') {\n mcpDebug('Skipping MCP configuration (mode: skip)')\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors: [],\n skipped: true,\n }\n }\n\n // 2. Detect available editors (filters out unparseable configs)\n const editors = await detectAvailableEditors()\n const detectedEditors = editors.map((e) => e.name)\n\n mcpDebug('Detected %d editors: %s', detectedEditors.length, detectedEditors)\n\n if (editors.length === 0) {\n if (explicit) {\n ux.warn(NO_EDITORS_DETECTED_MESSAGE)\n }\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // 3. Validate existing tokens against the Sanity API\n await validateEditorTokens(editors)\n\n // 4. Check if there's anything actionable\n const actionable = editors.filter((e) => !e.configured || e.authStatus !== 'valid')\n\n if (actionable.length === 0) {\n mcpDebug('All editors configured with valid credentials')\n const alreadyConfiguredEditors = editors\n .filter((e) => e.configured && e.authStatus === 'valid')\n .map((e) => e.name)\n if (explicit) {\n ux.stdout(`${logSymbols.success} All detected editors are already configured`)\n }\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // Non-actionable editors are already configured with valid credentials\n const alreadyConfiguredEditors = editors.filter((e) => !actionable.includes(e)).map((e) => e.name)\n\n // 5. Select editors to configure — prompt interactively or auto-select all\n const selected = mode === 'auto' ? actionable : await promptForMCPSetup(actionable)\n\n if (!selected || selected.length === 0) {\n // User deselected all editors\n ux.stdout('MCP configuration skipped')\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // 6. Get a token — reuse a valid existing one or create a new one\n let token: string | undefined\n\n // Look for an existing valid token we can reuse\n const validEditor = editors.find((e) => e.authStatus === 'valid' && e.existingToken)\n if (validEditor?.existingToken) {\n mcpDebug('Reusing valid token from %s', validEditor.name)\n token = validEditor.existingToken\n }\n\n const allOAuth = selected.every((e) => EDITOR_CONFIGS[e.name].oauthOnly)\n\n // Fall back to creating a new token\n // If all editors use OAuth, we don't need to create a token\n if (!token && !allOAuth) {\n try {\n token = await createMCPToken()\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error creating MCP token', error)\n ux.warn(`Could not configure MCP: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n error: err,\n skipped: false,\n }\n }\n }\n\n // 7. Write configs for each selected editor\n const configuredEditors: EditorName[] = []\n try {\n for (const editor of selected) {\n await writeMCPConfig(editor, token)\n configuredEditors.push(editor.name)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error writing MCP config', error)\n ux.warn(`Could not configure MCP: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n error: err,\n skipped: false,\n }\n }\n\n ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`)\n\n return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n skipped: false,\n }\n}\n"],"names":["ux","subdebug","logSymbols","createMCPToken","MCP_SERVER_URL","detectAvailableEditors","EDITOR_CONFIGS","promptForMCPSetup","validateEditorTokens","writeMCPConfig","mcpDebug","NO_EDITORS_DETECTED_MESSAGE","setupMCP","options","explicit","mode","alreadyConfiguredEditors","configuredEditors","detectedEditors","skipped","editors","map","e","name","length","warn","actionable","filter","configured","authStatus","stdout","success","includes","selected","token","validEditor","find","existingToken","allOAuth","every","oauthOnly","error","err","Error","String","message","editor","push","join"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/mcp/setupMCP.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {createMCPToken, MCP_SERVER_URL} from '../../services/mcp.js'\nimport {readSkillState} from '../skills/readSkillState.js'\nimport {SANITY_SKILL_NAME} from '../skills/setupSkills.js'\nimport {detectAvailableEditors} from './detectAvailableEditors.js'\nimport {\n EDITOR_CONFIGS,\n type EditorName,\n getSkillsCliAgent,\n getSkillsCliAgentDisplayName,\n} from './editorConfigs.js'\nimport {type EditorAction, type EditorChoice, promptForMCPSetup} from './promptForMCPSetup.js'\nimport {type Editor} from './types.js'\nimport {validateEditorTokens} from './validateEditorTokens.js'\nimport {writeMCPConfig} from './writeMCPConfig.js'\n\nconst mcpDebug = subdebug('mcp:setup')\n\nconst NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`\n\ntype Mode = 'auto' | 'prompt' | 'skip'\n\ninterface MCPSetupOptions {\n /**\n * Pre-detected editors. When omitted, `detectAvailableEditors()` is called.\n * Accepting this from the caller avoids re-running detection (which probes\n * the filesystem and shells out to CLI binaries) when the result is already\n * available — e.g. when `sanity init` runs both MCP and skills setup.\n */\n editors?: Editor[]\n\n /**\n * Whether the user explicitly requested MCP configuration (e.g. `sanity mcp configure`).\n * When true, shows status messages even when there's nothing to do.\n * When false/undefined (e.g. called from `sanity init`), stays quiet.\n */\n explicit?: boolean\n\n /**\n * Controls how MCP setup behaves:\n * - 'prompt': Ask the user which editors to configure (default)\n * - 'auto': Auto-configure all detected editors without prompting\n * - 'skip': Skip MCP configuration entirely\n */\n mode?: Mode\n\n /**\n * Controls whether skills install is also offered in the same prompt:\n * - 'prompt': Combine MCP + skills offers in one checkbox\n * - 'auto': Combine, but skip the prompt and select everything\n * - 'skip' (default): Skip skills entirely — today's MCP-only behavior\n */\n skillsMode?: Mode\n}\n\ninterface MCPSetupResult {\n /** Editors that were already configured with valid credentials (nothing to do) */\n alreadyConfiguredEditors: EditorName[]\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n /** Skills-CLI agent IDs that the caller should install. Deduplicated. */\n skillsToInstall: string[]\n skipped: boolean\n\n error?: Error\n}\n\ninterface ClassifiedEditor {\n action: 'none' | EditorAction\n editor: Editor\n}\n\n/**\n * Classify each editor into one of four actions based on MCP status and\n * whether a Sanity skill is already installed for its skills-CLI agent.\n */\nfunction classifyEditors(editors: Editor[]): ClassifiedEditor[] {\n return editors.map((editor) => {\n const needsMCP = !editor.configured || editor.authStatus !== 'valid'\n const skillsCliAgent = getSkillsCliAgent(editor.name)\n const hasSkillMapping = Boolean(skillsCliAgent)\n const skillInstalled = editor.skillInstalled === true\n\n if (needsMCP) {\n return {action: hasSkillMapping ? 'mcp-and-skill' : 'mcp-only', editor}\n }\n if (hasSkillMapping && !skillInstalled) {\n return {action: 'skill-only', editor}\n }\n return {action: 'none', editor}\n })\n}\n\n/**\n * Apply masking based on the configured modes. `skip` modes mute the\n * corresponding action so we never prompt for or run work the user opted out\n * of via `--no-mcp` / `--no-skills`.\n */\nfunction applyMasking(\n classified: ClassifiedEditor[],\n mcpMode: Mode,\n skillsMode: Mode,\n): EditorChoice[] {\n const actionable: EditorChoice[] = []\n\n for (const {action, editor} of classified) {\n if (action === 'none') continue\n\n if (mcpMode === 'skip' && skillsMode === 'skip') continue\n\n if (mcpMode === 'skip') {\n // No MCP writes — keep only skill-only / mcp-and-skill (downgraded to skill-only)\n if (action === 'mcp-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'skill-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n if (skillsMode === 'skip') {\n // No skill install — drop skill-only, downgrade mcp-and-skill → mcp-only\n if (action === 'skill-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'mcp-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n actionable.push({action, editor})\n }\n\n return actionable\n}\n\nfunction getPromptMessage(mcpMode: Mode, skillsMode: Mode): string {\n if (mcpMode === 'skip') return 'Install Sanity agent skills for these editors?'\n if (skillsMode === 'skip') return 'Configure Sanity MCP server?'\n return 'Configure Sanity MCP and install agent skills for these editors?'\n}\n\n/**\n * Main MCP setup orchestration.\n *\n * When `skillsMode !== 'skip'`, the prompt combines MCP and skill offers,\n * and the result includes `skillsToInstall` — agent IDs the caller should\n * install via `setupSkills`. `setupMCP` itself never installs skills.\n */\nexport async function setupMCP(options?: MCPSetupOptions): Promise<MCPSetupResult> {\n const {explicit = false, mode: mcpMode = 'prompt', skillsMode = 'skip'} = options ?? {}\n\n // 1. Both opted out → nothing to do.\n if (mcpMode === 'skip' && skillsMode === 'skip') {\n mcpDebug('Skipping setup (mcpMode: skip, skillsMode: skip)')\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors: [],\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 2. Detect available editors (filters out unparseable configs)\n const editors = options?.editors ?? (await detectAvailableEditors())\n const detectedEditors = editors.map((e) => e.name)\n\n mcpDebug('Detected %d editors: %s', detectedEditors.length, detectedEditors)\n\n if (editors.length === 0) {\n if (explicit) {\n ux.warn(NO_EDITORS_DETECTED_MESSAGE)\n }\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 3. Validate existing tokens against the Sanity API\n await validateEditorTokens(editors)\n\n // 4. Read skill state when skills are in scope so classification can dedup\n if (skillsMode !== 'skip') {\n const {installedAgentDisplayNames} = await readSkillState({skillName: SANITY_SKILL_NAME})\n for (const editor of editors) {\n const displayName = getSkillsCliAgentDisplayName(editor.name)\n editor.skillInstalled = displayName ? installedAgentDisplayNames.has(displayName) : false\n }\n }\n\n // 5. Classify + mask\n const classified = classifyEditors(editors)\n const actionable = applyMasking(classified, mcpMode, skillsMode)\n\n // \"Already configured\" surfaces editors whose MCP setup is valid (skill\n // state doesn't matter for this signal — that's what skillsToInstall is for).\n const actionableNames = new Set(actionable.map((c) => c.editor.name))\n const alreadyConfiguredEditors = editors\n .filter((e) => e.configured && e.authStatus === 'valid' && !actionableNames.has(e.name))\n .map((e) => e.name)\n\n if (actionable.length === 0) {\n mcpDebug('Nothing actionable after classification + masking')\n if (explicit) {\n ux.stdout(`${logSymbols.success} All detected editors are already configured`)\n }\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 6. Select editors to configure — prompt interactively or auto-select all.\n // We only auto when neither side wants a prompt: MCP auto, or (MCP skip\n // + skills auto). Anything that asks `mode: 'prompt'` for MCP wins the\n // prompt even when skills would have auto-installed.\n const shouldAuto = mcpMode === 'auto' || (mcpMode === 'skip' && skillsMode === 'auto')\n const selected = shouldAuto\n ? actionable\n : await promptForMCPSetup({\n choices: actionable,\n message: getPromptMessage(mcpMode, skillsMode),\n })\n\n if (!selected || selected.length === 0) {\n ux.stdout('MCP configuration skipped')\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 7. MCP write phase — only for choices that need MCP\n const mcpSelected = selected.filter(\n (c) => c.action === 'mcp-only' || c.action === 'mcp-and-skill',\n )\n\n let token: string | undefined\n const configuredEditors: EditorName[] = []\n let mcpError: Error | undefined\n\n if (mcpSelected.length > 0) {\n const validEditor = editors.find((e) => e.authStatus === 'valid' && e.existingToken)\n if (validEditor?.existingToken) {\n mcpDebug('Reusing valid token from %s', validEditor.name)\n token = validEditor.existingToken\n }\n\n const allOAuth = mcpSelected.every((c) => EDITOR_CONFIGS[c.editor.name].oauthOnly)\n\n if (!token && !allOAuth) {\n try {\n token = await createMCPToken()\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error creating MCP token', error)\n ux.warn(`Could not configure MCP: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n mcpError = err\n }\n }\n\n if (!mcpError) {\n for (const choice of mcpSelected) {\n try {\n await writeMCPConfig(choice.editor, token)\n configuredEditors.push(choice.editor.name)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error writing MCP config for %s: %O', choice.editor.name, error)\n ux.warn(`Could not configure MCP for ${choice.editor.name}: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n mcpError = err\n }\n }\n }\n\n if (configuredEditors.length > 0) {\n ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`)\n }\n }\n\n // 8. Build skillsToInstall — only for choices the user kept, only when the\n // associated MCP write succeeded (or wasn't needed).\n const skillsToInstall: string[] = []\n if (skillsMode !== 'skip') {\n for (const choice of selected) {\n if (choice.action === 'skill-only') {\n const agent = getSkillsCliAgent(choice.editor.name)\n if (agent) skillsToInstall.push(agent)\n continue\n }\n if (choice.action === 'mcp-and-skill' && configuredEditors.includes(choice.editor.name)) {\n const agent = getSkillsCliAgent(choice.editor.name)\n if (agent) skillsToInstall.push(agent)\n }\n }\n }\n\n return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n error: mcpError,\n skillsToInstall: [...new Set(skillsToInstall)],\n skipped: false,\n }\n}\n"],"names":["ux","subdebug","logSymbols","createMCPToken","MCP_SERVER_URL","readSkillState","SANITY_SKILL_NAME","detectAvailableEditors","EDITOR_CONFIGS","getSkillsCliAgent","getSkillsCliAgentDisplayName","promptForMCPSetup","validateEditorTokens","writeMCPConfig","mcpDebug","NO_EDITORS_DETECTED_MESSAGE","classifyEditors","editors","map","editor","needsMCP","configured","authStatus","skillsCliAgent","name","hasSkillMapping","Boolean","skillInstalled","action","applyMasking","classified","mcpMode","skillsMode","actionable","push","getPromptMessage","setupMCP","options","explicit","mode","alreadyConfiguredEditors","configuredEditors","detectedEditors","skillsToInstall","skipped","e","length","warn","installedAgentDisplayNames","skillName","displayName","has","actionableNames","Set","c","filter","stdout","success","shouldAuto","selected","choices","message","mcpSelected","token","mcpError","validEditor","find","existingToken","allOAuth","every","oauthOnly","error","err","Error","String","choice","join","agent","includes"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/mcp/types.ts"],"sourcesContent":["import {type EditorName} from './editorConfigs.js'\n\n/** Auth credential status for a configured editor */\nexport type AuthStatus = 'unauthorized' | 'valid'\n\nexport interface Editor {\n configPath: string\n /** Whether Sanity MCP is already configured for this editor */\n configured: boolean\n name: EditorName\n\n /**\n * Auth status of the existing token. Only set for editors that have\n * a Sanity MCP config with a token that has been validated against the API.\n */\n authStatus?: AuthStatus\n /** The existing auth token found in the editor config, if any */\n existingToken?: string\n}\n"],"names":[],"mappings":"AAKA,WAaC"}
1
+ {"version":3,"sources":["../../../src/actions/mcp/types.ts"],"sourcesContent":["import {type EditorName} from './editorConfigs.js'\n\n/** Auth credential status for a configured editor */\nexport type AuthStatus = 'unauthorized' | 'valid'\n\nexport interface Editor {\n configPath: string\n /** Whether Sanity MCP is already configured for this editor */\n configured: boolean\n name: EditorName\n\n /**\n * Auth status of the existing token. Only set for editors that have\n * a Sanity MCP config with a token that has been validated against the API.\n */\n authStatus?: AuthStatus\n /** The existing auth token found in the editor config, if any */\n existingToken?: string\n /**\n * Whether the Sanity agent skill is already installed globally for this\n * editor's skills-CLI agent. Populated during setup classification when\n * skill state has been probed; absent when skill installation isn't being\n * considered (e.g. `mcp configure`).\n */\n skillInstalled?: boolean\n}\n"],"names":[],"mappings":"AAKA,WAoBC"}
@@ -1,6 +1,6 @@
1
+ import { SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
1
2
  import { studioWorkerTask } from '@sanity/cli-core';
2
3
  import { updateWorkspacesSchemas } from './updateWorkspaceSchema.js';
3
- import { SchemaExtractionError } from './utils/SchemaExtractionError.js';
4
4
  export async function deploySchemas(options) {
5
5
  const { tag, verbose, workDir, workspaceName } = options;
6
6
  const result = await studioWorkerTask(new URL('extractSanityWorkspace.worker.js', import.meta.url), {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/deploySchemas.ts"],"sourcesContent":["import {studioWorkerTask} from '@sanity/cli-core'\nimport {type SchemaValidationProblemGroup} from '@sanity/types'\nimport {type Workspace} from 'sanity'\n\nimport {type ManifestSchemaType} from '../manifest/types.js'\nimport {type ExtractWorkspaceWorkerData} from './types.js'\nimport {updateWorkspacesSchemas, type WorkspaceSchemaInput} from './updateWorkspaceSchema.js'\nimport {SchemaExtractionError} from './utils/SchemaExtractionError.js'\n\ninterface DeploySchemasOptions {\n verbose: boolean\n workDir: string\n\n tag?: string\n workspaceName?: string\n}\n\ntype WorkspaceWithManifest = Workspace & {manifestSchema: ManifestSchemaType[]}\n\ntype ExtractWorkspaceWorkerMessage =\n | {\n error: string\n type: 'error'\n validation?: SchemaValidationProblemGroup[]\n }\n | {\n type: 'success'\n workspaces: WorkspaceWithManifest[]\n }\n\nexport async function deploySchemas(options: DeploySchemasOptions): Promise<void> {\n const {tag, verbose, workDir, workspaceName} = options\n\n const result = await studioWorkerTask<ExtractWorkspaceWorkerMessage>(\n new URL('extractSanityWorkspace.worker.js', import.meta.url),\n {\n name: 'extractSanityWorkspace',\n studioRootPath: workDir,\n workerData: {\n configPath: workDir,\n workDir,\n } satisfies ExtractWorkspaceWorkerData,\n },\n )\n\n if (result.type === 'error') {\n throw new SchemaExtractionError(result.error, result.validation)\n }\n\n const workspaces: WorkspaceSchemaInput[] = result.workspaces\n .filter((workspace) => !workspaceName || workspace.name === workspaceName)\n .map((workspace) => ({\n dataset: workspace.dataset,\n manifestSchema: workspace.manifestSchema,\n name: workspace.name,\n projectId: workspace.projectId,\n title: workspace.title,\n }))\n\n if (workspaces.length === 0) {\n const error = workspaceName\n ? new Error(`Found no workspaces named \"${workspaceName}\"`)\n : new Error('No workspaces found')\n throw error\n }\n\n await updateWorkspacesSchemas({\n tag,\n verbose,\n workspaces,\n })\n}\n"],"names":["studioWorkerTask","updateWorkspacesSchemas","SchemaExtractionError","deploySchemas","options","tag","verbose","workDir","workspaceName","result","URL","url","name","studioRootPath","workerData","configPath","type","error","validation","workspaces","filter","workspace","map","dataset","manifestSchema","projectId","title","length","Error"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,mBAAkB;AAMjD,SAAQC,uBAAuB,QAAkC,6BAA4B;AAC7F,SAAQC,qBAAqB,QAAO,mCAAkC;AAuBtE,OAAO,eAAeC,cAAcC,OAA6B;IAC/D,MAAM,EAACC,GAAG,EAAEC,OAAO,EAAEC,OAAO,EAAEC,aAAa,EAAC,GAAGJ;IAE/C,MAAMK,SAAS,MAAMT,iBACnB,IAAIU,IAAI,oCAAoC,YAAYC,GAAG,GAC3D;QACEC,MAAM;QACNC,gBAAgBN;QAChBO,YAAY;YACVC,YAAYR;YACZA;QACF;IACF;IAGF,IAAIE,OAAOO,IAAI,KAAK,SAAS;QAC3B,MAAM,IAAId,sBAAsBO,OAAOQ,KAAK,EAAER,OAAOS,UAAU;IACjE;IAEA,MAAMC,aAAqCV,OAAOU,UAAU,CACzDC,MAAM,CAAC,CAACC,YAAc,CAACb,iBAAiBa,UAAUT,IAAI,KAAKJ,eAC3Dc,GAAG,CAAC,CAACD,YAAe,CAAA;YACnBE,SAASF,UAAUE,OAAO;YAC1BC,gBAAgBH,UAAUG,cAAc;YACxCZ,MAAMS,UAAUT,IAAI;YACpBa,WAAWJ,UAAUI,SAAS;YAC9BC,OAAOL,UAAUK,KAAK;QACxB,CAAA;IAEF,IAAIP,WAAWQ,MAAM,KAAK,GAAG;QAC3B,MAAMV,QAAQT,gBACV,IAAIoB,MAAM,CAAC,2BAA2B,EAAEpB,cAAc,CAAC,CAAC,IACxD,IAAIoB,MAAM;QACd,MAAMX;IACR;IAEA,MAAMhB,wBAAwB;QAC5BI;QACAC;QACAa;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/schema/deploySchemas.ts"],"sourcesContent":["import {SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {studioWorkerTask} from '@sanity/cli-core'\nimport {type SchemaValidationProblemGroup} from '@sanity/types'\nimport {type Workspace} from 'sanity'\n\nimport {type ManifestSchemaType} from '../manifest/types.js'\nimport {type ExtractWorkspaceWorkerData} from './types.js'\nimport {updateWorkspacesSchemas, type WorkspaceSchemaInput} from './updateWorkspaceSchema.js'\n\ninterface DeploySchemasOptions {\n verbose: boolean\n workDir: string\n\n tag?: string\n workspaceName?: string\n}\n\ntype WorkspaceWithManifest = Workspace & {manifestSchema: ManifestSchemaType[]}\n\ntype ExtractWorkspaceWorkerMessage =\n | {\n error: string\n type: 'error'\n validation?: SchemaValidationProblemGroup[]\n }\n | {\n type: 'success'\n workspaces: WorkspaceWithManifest[]\n }\n\nexport async function deploySchemas(options: DeploySchemasOptions): Promise<void> {\n const {tag, verbose, workDir, workspaceName} = options\n\n const result = await studioWorkerTask<ExtractWorkspaceWorkerMessage>(\n new URL('extractSanityWorkspace.worker.js', import.meta.url),\n {\n name: 'extractSanityWorkspace',\n studioRootPath: workDir,\n workerData: {\n configPath: workDir,\n workDir,\n } satisfies ExtractWorkspaceWorkerData,\n },\n )\n\n if (result.type === 'error') {\n throw new SchemaExtractionError(result.error, result.validation)\n }\n\n const workspaces: WorkspaceSchemaInput[] = result.workspaces\n .filter((workspace) => !workspaceName || workspace.name === workspaceName)\n .map((workspace) => ({\n dataset: workspace.dataset,\n manifestSchema: workspace.manifestSchema,\n name: workspace.name,\n projectId: workspace.projectId,\n title: workspace.title,\n }))\n\n if (workspaces.length === 0) {\n const error = workspaceName\n ? new Error(`Found no workspaces named \"${workspaceName}\"`)\n : new Error('No workspaces found')\n throw error\n }\n\n await updateWorkspacesSchemas({\n tag,\n verbose,\n workspaces,\n })\n}\n"],"names":["SchemaExtractionError","studioWorkerTask","updateWorkspacesSchemas","deploySchemas","options","tag","verbose","workDir","workspaceName","result","URL","url","name","studioRootPath","workerData","configPath","type","error","validation","workspaces","filter","workspace","map","dataset","manifestSchema","projectId","title","length","Error"],"mappings":"AAAA,SAAQA,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,gBAAgB,QAAO,mBAAkB;AAMjD,SAAQC,uBAAuB,QAAkC,6BAA4B;AAuB7F,OAAO,eAAeC,cAAcC,OAA6B;IAC/D,MAAM,EAACC,GAAG,EAAEC,OAAO,EAAEC,OAAO,EAAEC,aAAa,EAAC,GAAGJ;IAE/C,MAAMK,SAAS,MAAMR,iBACnB,IAAIS,IAAI,oCAAoC,YAAYC,GAAG,GAC3D;QACEC,MAAM;QACNC,gBAAgBN;QAChBO,YAAY;YACVC,YAAYR;YACZA;QACF;IACF;IAGF,IAAIE,OAAOO,IAAI,KAAK,SAAS;QAC3B,MAAM,IAAIhB,sBAAsBS,OAAOQ,KAAK,EAAER,OAAOS,UAAU;IACjE;IAEA,MAAMC,aAAqCV,OAAOU,UAAU,CACzDC,MAAM,CAAC,CAACC,YAAc,CAACb,iBAAiBa,UAAUT,IAAI,KAAKJ,eAC3Dc,GAAG,CAAC,CAACD,YAAe,CAAA;YACnBE,SAASF,UAAUE,OAAO;YAC1BC,gBAAgBH,UAAUG,cAAc;YACxCZ,MAAMS,UAAUT,IAAI;YACpBa,WAAWJ,UAAUI,SAAS;YAC9BC,OAAOL,UAAUK,KAAK;QACxB,CAAA;IAEF,IAAIP,WAAWQ,MAAM,KAAK,GAAG;QAC3B,MAAMV,QAAQT,gBACV,IAAIoB,MAAM,CAAC,2BAA2B,EAAEpB,cAAc,CAAC,CAAC,IACxD,IAAIoB,MAAM;QACd,MAAMX;IACR;IAEA,MAAMf,wBAAwB;QAC5BG;QACAC;QACAa;IACF;AACF"}
@@ -1,8 +1,8 @@
1
1
  import { isMainThread, parentPort, workerData } from 'node:worker_threads';
2
+ import { extractValidationFromSchemaError } from '@sanity/cli-build/_internal/extract';
2
3
  import { getStudioWorkspaces, safeStructuredClone } from '@sanity/cli-core';
3
4
  import { extractManifestSchemaTypes } from '../manifest/extractWorkspaceManifest.js';
4
5
  import { extractWorkspaceWorkerData } from './types.js';
5
- import { extractValidationFromSchemaError } from './utils/extractValidationFromSchemaError.js';
6
6
  if (isMainThread || !parentPort) {
7
7
  throw new Error('Should only be run in a worker!');
8
8
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/extractSanityWorkspace.worker.ts"],"sourcesContent":["import {isMainThread, parentPort, workerData} from 'node:worker_threads'\n\nimport {getStudioWorkspaces, safeStructuredClone} from '@sanity/cli-core'\nimport {type Schema} from '@sanity/types'\n\nimport {extractManifestSchemaTypes} from '../manifest/extractWorkspaceManifest.js'\nimport {extractWorkspaceWorkerData} from './types.js'\nimport {extractValidationFromSchemaError} from './utils/extractValidationFromSchemaError.js'\n\nif (isMainThread || !parentPort) {\n throw new Error('Should only be run in a worker!')\n}\n\nconst {configPath, workDir} = extractWorkspaceWorkerData.parse(workerData)\n\ntry {\n const workspaces = await getStudioWorkspaces(configPath)\n\n // Extract manifest schemas while Schema objects are still live (before structured clone\n // strips class methods like getTypeNames/get). The API expects ManifestSchemaType[], not\n // the runtime Schema class instance.\n const workspacesWithManifest = await Promise.all(\n workspaces.map(async (workspace) => ({\n ...safeStructuredClone(workspace),\n manifestSchema: await extractManifestSchemaTypes(workspace.schema as Schema, workDir),\n })),\n )\n\n parentPort.postMessage({\n type: 'success',\n workspaces: workspacesWithManifest,\n })\n} catch (error) {\n const validation = await extractValidationFromSchemaError(error, workDir)\n parentPort.postMessage({\n error: error instanceof Error ? error.message : String(error),\n type: 'error',\n validation,\n })\n}\n"],"names":["isMainThread","parentPort","workerData","getStudioWorkspaces","safeStructuredClone","extractManifestSchemaTypes","extractWorkspaceWorkerData","extractValidationFromSchemaError","Error","configPath","workDir","parse","workspaces","workspacesWithManifest","Promise","all","map","workspace","manifestSchema","schema","postMessage","type","error","validation","message","String"],"mappings":"AAAA,SAAQA,YAAY,EAAEC,UAAU,EAAEC,UAAU,QAAO,sBAAqB;AAExE,SAAQC,mBAAmB,EAAEC,mBAAmB,QAAO,mBAAkB;AAGzE,SAAQC,0BAA0B,QAAO,0CAAyC;AAClF,SAAQC,0BAA0B,QAAO,aAAY;AACrD,SAAQC,gCAAgC,QAAO,8CAA6C;AAE5F,IAAIP,gBAAgB,CAACC,YAAY;IAC/B,MAAM,IAAIO,MAAM;AAClB;AAEA,MAAM,EAACC,UAAU,EAAEC,OAAO,EAAC,GAAGJ,2BAA2BK,KAAK,CAACT;AAE/D,IAAI;IACF,MAAMU,aAAa,MAAMT,oBAAoBM;IAE7C,wFAAwF;IACxF,yFAAyF;IACzF,qCAAqC;IACrC,MAAMI,yBAAyB,MAAMC,QAAQC,GAAG,CAC9CH,WAAWI,GAAG,CAAC,OAAOC,YAAe,CAAA;YACnC,GAAGb,oBAAoBa,UAAU;YACjCC,gBAAgB,MAAMb,2BAA2BY,UAAUE,MAAM,EAAYT;QAC/E,CAAA;IAGFT,WAAWmB,WAAW,CAAC;QACrBC,MAAM;QACNT,YAAYC;IACd;AACF,EAAE,OAAOS,OAAO;IACd,MAAMC,aAAa,MAAMhB,iCAAiCe,OAAOZ;IACjET,WAAWmB,WAAW,CAAC;QACrBE,OAAOA,iBAAiBd,QAAQc,MAAME,OAAO,GAAGC,OAAOH;QACvDD,MAAM;QACNE;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/schema/extractSanityWorkspace.worker.ts"],"sourcesContent":["import {isMainThread, parentPort, workerData} from 'node:worker_threads'\n\nimport {extractValidationFromSchemaError} from '@sanity/cli-build/_internal/extract'\nimport {getStudioWorkspaces, safeStructuredClone} from '@sanity/cli-core'\nimport {type Schema} from '@sanity/types'\n\nimport {extractManifestSchemaTypes} from '../manifest/extractWorkspaceManifest.js'\nimport {extractWorkspaceWorkerData} from './types.js'\n\nif (isMainThread || !parentPort) {\n throw new Error('Should only be run in a worker!')\n}\n\nconst {configPath, workDir} = extractWorkspaceWorkerData.parse(workerData)\n\ntry {\n const workspaces = await getStudioWorkspaces(configPath)\n\n // Extract manifest schemas while Schema objects are still live (before structured clone\n // strips class methods like getTypeNames/get). The API expects ManifestSchemaType[], not\n // the runtime Schema class instance.\n const workspacesWithManifest = await Promise.all(\n workspaces.map(async (workspace) => ({\n ...safeStructuredClone(workspace),\n manifestSchema: await extractManifestSchemaTypes(workspace.schema as Schema, workDir),\n })),\n )\n\n parentPort.postMessage({\n type: 'success',\n workspaces: workspacesWithManifest,\n })\n} catch (error) {\n const validation = await extractValidationFromSchemaError(error, workDir)\n parentPort.postMessage({\n error: error instanceof Error ? error.message : String(error),\n type: 'error',\n validation,\n })\n}\n"],"names":["isMainThread","parentPort","workerData","extractValidationFromSchemaError","getStudioWorkspaces","safeStructuredClone","extractManifestSchemaTypes","extractWorkspaceWorkerData","Error","configPath","workDir","parse","workspaces","workspacesWithManifest","Promise","all","map","workspace","manifestSchema","schema","postMessage","type","error","validation","message","String"],"mappings":"AAAA,SAAQA,YAAY,EAAEC,UAAU,EAAEC,UAAU,QAAO,sBAAqB;AAExE,SAAQC,gCAAgC,QAAO,sCAAqC;AACpF,SAAQC,mBAAmB,EAAEC,mBAAmB,QAAO,mBAAkB;AAGzE,SAAQC,0BAA0B,QAAO,0CAAyC;AAClF,SAAQC,0BAA0B,QAAO,aAAY;AAErD,IAAIP,gBAAgB,CAACC,YAAY;IAC/B,MAAM,IAAIO,MAAM;AAClB;AAEA,MAAM,EAACC,UAAU,EAAEC,OAAO,EAAC,GAAGH,2BAA2BI,KAAK,CAACT;AAE/D,IAAI;IACF,MAAMU,aAAa,MAAMR,oBAAoBK;IAE7C,wFAAwF;IACxF,yFAAyF;IACzF,qCAAqC;IACrC,MAAMI,yBAAyB,MAAMC,QAAQC,GAAG,CAC9CH,WAAWI,GAAG,CAAC,OAAOC,YAAe,CAAA;YACnC,GAAGZ,oBAAoBY,UAAU;YACjCC,gBAAgB,MAAMZ,2BAA2BW,UAAUE,MAAM,EAAYT;QAC/E,CAAA;IAGFT,WAAWmB,WAAW,CAAC;QACrBC,MAAM;QACNT,YAAYC;IACd;AACF,EAAE,OAAOS,OAAO;IACd,MAAMC,aAAa,MAAMpB,iCAAiCmB,OAAOZ;IACjET,WAAWmB,WAAW,CAAC;QACrBE,OAAOA,iBAAiBd,QAAQc,MAAME,OAAO,GAAGC,OAAOH;QACvDD,MAAM;QACNE;IACF;AACF"}
@@ -1,11 +1,8 @@
1
1
  import { exit } from '@oclif/core/errors';
2
+ import { formatSchemaValidation, runSchemaExtraction, SchemaExtractedTrace, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
2
3
  import { getCliTelemetry } from '@sanity/cli-core';
3
4
  import { spinner } from '@sanity/cli-core/ux';
4
- import { SchemaExtractedTrace } from '../../telemetry/extractSchema.telemetry.js';
5
- import { formatSchemaValidation } from './formatSchemaValidation.js';
6
- import { runSchemaExtraction } from './runSchemaExtraction.js';
7
5
  import { schemasExtractDebug } from './utils/debug.js';
8
- import { SchemaExtractionError } from './utils/SchemaExtractionError.js';
9
6
  export async function extractSchema(options) {
10
7
  const { extractOptions, output } = options;
11
8
  const { enforceRequiredFields, format, outputPath } = extractOptions;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/extractSchema.ts"],"sourcesContent":["import {exit} from '@oclif/core/errors'\nimport {getCliTelemetry, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {SchemaExtractedTrace} from '../../telemetry/extractSchema.telemetry.js'\nimport {formatSchemaValidation} from './formatSchemaValidation.js'\nimport {type ExtractOptions} from './getExtractOptions.js'\nimport {runSchemaExtraction} from './runSchemaExtraction.js'\nimport {schemasExtractDebug} from './utils/debug.js'\nimport {SchemaExtractionError} from './utils/SchemaExtractionError.js'\n\ninterface ExtractSchemaActionOptions {\n extractOptions: ExtractOptions\n output: Output\n}\n\nexport async function extractSchema(options: ExtractSchemaActionOptions): Promise<void> {\n const {extractOptions, output} = options\n const {enforceRequiredFields, format, outputPath} = extractOptions\n\n const spin = spinner(\n enforceRequiredFields ? 'Extracting schema with enforced required fields' : 'Extracting schema',\n ).start()\n\n const trace = getCliTelemetry().trace(SchemaExtractedTrace)\n trace.start()\n\n try {\n spin.text = `Writing schema to ${outputPath}`\n\n const schema = await runSchemaExtraction(extractOptions)\n\n trace.log({\n enforceRequiredFields,\n schemaAllTypesCount: schema.length,\n schemaDocumentTypesCount: schema.filter((type) => type.type === 'document').length,\n schemaFormat: format,\n schemaTypesCount: schema.filter((type) => type.type === 'type').length,\n })\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n trace.complete()\n } catch (err) {\n trace.error(err)\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail(\n enforceRequiredFields\n ? 'Failed to extract schema with enforced required fields'\n : 'Failed to extract schema',\n )\n\n // Display validation errors if available and exit\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n exit(1)\n }\n\n output.error(err instanceof Error ? err.message : 'Failed to extract schema', {exit: 1})\n }\n}\n"],"names":["exit","getCliTelemetry","spinner","SchemaExtractedTrace","formatSchemaValidation","runSchemaExtraction","schemasExtractDebug","SchemaExtractionError","extractSchema","options","extractOptions","output","enforceRequiredFields","format","outputPath","spin","start","trace","text","schema","log","schemaAllTypesCount","length","schemaDocumentTypesCount","filter","type","schemaFormat","schemaTypesCount","succeed","complete","err","error","fail","validation","Error","message"],"mappings":"AAAA,SAAQA,IAAI,QAAO,qBAAoB;AACvC,SAAQC,eAAe,QAAoB,mBAAkB;AAC7D,SAAQC,OAAO,QAAO,sBAAqB;AAE3C,SAAQC,oBAAoB,QAAO,6CAA4C;AAC/E,SAAQC,sBAAsB,QAAO,8BAA6B;AAElE,SAAQC,mBAAmB,QAAO,2BAA0B;AAC5D,SAAQC,mBAAmB,QAAO,mBAAkB;AACpD,SAAQC,qBAAqB,QAAO,mCAAkC;AAOtE,OAAO,eAAeC,cAAcC,OAAmC;IACrE,MAAM,EAACC,cAAc,EAAEC,MAAM,EAAC,GAAGF;IACjC,MAAM,EAACG,qBAAqB,EAAEC,MAAM,EAAEC,UAAU,EAAC,GAAGJ;IAEpD,MAAMK,OAAOb,QACXU,wBAAwB,oDAAoD,qBAC5EI,KAAK;IAEP,MAAMC,QAAQhB,kBAAkBgB,KAAK,CAACd;IACtCc,MAAMD,KAAK;IAEX,IAAI;QACFD,KAAKG,IAAI,GAAG,CAAC,kBAAkB,EAAEJ,YAAY;QAE7C,MAAMK,SAAS,MAAMd,oBAAoBK;QAEzCO,MAAMG,GAAG,CAAC;YACRR;YACAS,qBAAqBF,OAAOG,MAAM;YAClCC,0BAA0BJ,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,YAAYH,MAAM;YAClFI,cAAcb;YACdc,kBAAkBR,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,QAAQH,MAAM;QACxE;QAEAP,KAAKa,OAAO,CACVhB,wBACI,CAAC,oBAAoB,EAAEE,WAAW,8BAA8B,CAAC,GACjE,CAAC,oBAAoB,EAAEA,YAAY;QAGzCG,MAAMY,QAAQ;IAChB,EAAE,OAAOC,KAAK;QACZb,MAAMc,KAAK,CAACD;QACZxB,oBAAoB,4BAA4BwB;QAChDf,KAAKiB,IAAI,CACPpB,wBACI,2DACA;QAGN,kDAAkD;QAClD,IAAIkB,eAAevB,yBAAyBuB,IAAIG,UAAU,IAAIH,IAAIG,UAAU,CAACX,MAAM,GAAG,GAAG;YACvFX,OAAOS,GAAG,CAAC;YACXT,OAAOS,GAAG,CAAChB,uBAAuB0B,IAAIG,UAAU;YAChDjC,KAAK;QACP;QAEAW,OAAOoB,KAAK,CAACD,eAAeI,QAAQJ,IAAIK,OAAO,GAAG,4BAA4B;YAACnC,MAAM;QAAC;IACxF;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/schema/extractSchema.ts"],"sourcesContent":["import {exit} from '@oclif/core/errors'\nimport {\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractedTrace,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {getCliTelemetry, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\ninterface ExtractSchemaActionOptions {\n extractOptions: ExtractOptions\n output: Output\n}\n\nexport async function extractSchema(options: ExtractSchemaActionOptions): Promise<void> {\n const {extractOptions, output} = options\n const {enforceRequiredFields, format, outputPath} = extractOptions\n\n const spin = spinner(\n enforceRequiredFields ? 'Extracting schema with enforced required fields' : 'Extracting schema',\n ).start()\n\n const trace = getCliTelemetry().trace(SchemaExtractedTrace)\n trace.start()\n\n try {\n spin.text = `Writing schema to ${outputPath}`\n\n const schema = await runSchemaExtraction(extractOptions)\n\n trace.log({\n enforceRequiredFields,\n schemaAllTypesCount: schema.length,\n schemaDocumentTypesCount: schema.filter((type) => type.type === 'document').length,\n schemaFormat: format,\n schemaTypesCount: schema.filter((type) => type.type === 'type').length,\n })\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n trace.complete()\n } catch (err) {\n trace.error(err)\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail(\n enforceRequiredFields\n ? 'Failed to extract schema with enforced required fields'\n : 'Failed to extract schema',\n )\n\n // Display validation errors if available and exit\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n exit(1)\n }\n\n output.error(err instanceof Error ? err.message : 'Failed to extract schema', {exit: 1})\n }\n}\n"],"names":["exit","formatSchemaValidation","runSchemaExtraction","SchemaExtractedTrace","SchemaExtractionError","getCliTelemetry","spinner","schemasExtractDebug","extractSchema","options","extractOptions","output","enforceRequiredFields","format","outputPath","spin","start","trace","text","schema","log","schemaAllTypesCount","length","schemaDocumentTypesCount","filter","type","schemaFormat","schemaTypesCount","succeed","complete","err","error","fail","validation","Error","message"],"mappings":"AAAA,SAAQA,IAAI,QAAO,qBAAoB;AACvC,SAEEC,sBAAsB,EACtBC,mBAAmB,EACnBC,oBAAoB,EACpBC,qBAAqB,QAChB,sCAAqC;AAC5C,SAAQC,eAAe,QAAoB,mBAAkB;AAC7D,SAAQC,OAAO,QAAO,sBAAqB;AAE3C,SAAQC,mBAAmB,QAAO,mBAAkB;AAOpD,OAAO,eAAeC,cAAcC,OAAmC;IACrE,MAAM,EAACC,cAAc,EAAEC,MAAM,EAAC,GAAGF;IACjC,MAAM,EAACG,qBAAqB,EAAEC,MAAM,EAAEC,UAAU,EAAC,GAAGJ;IAEpD,MAAMK,OAAOT,QACXM,wBAAwB,oDAAoD,qBAC5EI,KAAK;IAEP,MAAMC,QAAQZ,kBAAkBY,KAAK,CAACd;IACtCc,MAAMD,KAAK;IAEX,IAAI;QACFD,KAAKG,IAAI,GAAG,CAAC,kBAAkB,EAAEJ,YAAY;QAE7C,MAAMK,SAAS,MAAMjB,oBAAoBQ;QAEzCO,MAAMG,GAAG,CAAC;YACRR;YACAS,qBAAqBF,OAAOG,MAAM;YAClCC,0BAA0BJ,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,YAAYH,MAAM;YAClFI,cAAcb;YACdc,kBAAkBR,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,QAAQH,MAAM;QACxE;QAEAP,KAAKa,OAAO,CACVhB,wBACI,CAAC,oBAAoB,EAAEE,WAAW,8BAA8B,CAAC,GACjE,CAAC,oBAAoB,EAAEA,YAAY;QAGzCG,MAAMY,QAAQ;IAChB,EAAE,OAAOC,KAAK;QACZb,MAAMc,KAAK,CAACD;QACZvB,oBAAoB,4BAA4BuB;QAChDf,KAAKiB,IAAI,CACPpB,wBACI,2DACA;QAGN,kDAAkD;QAClD,IAAIkB,eAAe1B,yBAAyB0B,IAAIG,UAAU,IAAIH,IAAIG,UAAU,CAACX,MAAM,GAAG,GAAG;YACvFX,OAAOS,GAAG,CAAC;YACXT,OAAOS,GAAG,CAACnB,uBAAuB6B,IAAIG,UAAU;YAChDjC,KAAK;QACP;QAEAW,OAAOoB,KAAK,CAACD,eAAeI,QAAQJ,IAAIK,OAAO,GAAG,4BAA4B;YAACnC,MAAM;QAAC;IACxF;AACF"}
@@ -1,12 +1,9 @@
1
1
  import { dirname, isAbsolute, join, relative } from 'node:path';
2
+ import { createSchemaPatternMatcher, formatSchemaValidation, runSchemaExtraction, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
2
3
  import { spinner } from '@sanity/cli-core/ux';
3
4
  import { watch as chokidarWatch } from 'chokidar';
4
5
  import debounce from 'lodash-es/debounce.js';
5
- import { formatSchemaValidation } from './formatSchemaValidation.js';
6
- import { createSchemaPatternMatcher } from './matchSchemaPattern.js';
7
- import { runSchemaExtraction } from './runSchemaExtraction.js';
8
6
  import { schemasExtractDebug } from './utils/debug.js';
9
- import { SchemaExtractionError } from './utils/SchemaExtractionError.js';
10
7
  /** Default glob patterns to watch for schema changes */ export const DEFAULT_WATCH_PATTERNS = [
11
8
  'sanity.config.{js,jsx,ts,tsx,mjs}',
12
9
  'schema*/**/*.{js,jsx,ts,tsx,mjs}'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/extractSchemaWatcher.ts"],"sourcesContent":["import {dirname, isAbsolute, join, relative} from 'node:path'\n\nimport {type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {watch as chokidarWatch, type FSWatcher} from 'chokidar'\nimport debounce from 'lodash-es/debounce.js'\n\nimport {formatSchemaValidation} from './formatSchemaValidation.js'\nimport {type ExtractOptions} from './getExtractOptions.js'\nimport {createSchemaPatternMatcher} from './matchSchemaPattern.js'\nimport {runSchemaExtraction} from './runSchemaExtraction.js'\nimport {schemasExtractDebug} from './utils/debug.js'\nimport {SchemaExtractionError} from './utils/SchemaExtractionError.js'\n\n/** Default glob patterns to watch for schema changes */\nexport const DEFAULT_WATCH_PATTERNS = [\n 'sanity.config.{js,jsx,ts,tsx,mjs}',\n 'schema*/**/*.{js,jsx,ts,tsx,mjs}',\n]\n\n/** Default patterns to ignore when watching */\nconst IGNORED_PATTERNS = [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/lib/**',\n '**/.sanity/**',\n]\n\ninterface ExtractSchemaWatcherOptions {\n extractOptions: ExtractOptions\n output: Output\n watchPatterns: string[]\n\n onExtraction?: (result: {duration: number; success: boolean}) => void\n}\n\ninterface ExtractSchemaWatcher {\n close: () => Promise<void>\n watcher: FSWatcher\n}\n\n/** State for tracking extraction status */\ninterface WatchState {\n isExtracting: boolean\n pendingExtraction: boolean\n}\n\n/** Return type for createExtractionRunner */\ninterface ExtractionRunner {\n runExtraction: () => Promise<void>\n state: WatchState\n}\n\n/**\n * Creates an extraction runner with concurrency control.\n * If extraction is already running, queues one more extraction to run after completion.\n * Multiple queued requests are coalesced into a single pending extraction.\n */\nfunction createExtractionRunner(onExtract: () => Promise<void>): ExtractionRunner {\n const state: WatchState = {\n isExtracting: false,\n pendingExtraction: false,\n }\n\n async function runExtraction(): Promise<void> {\n if (state.isExtracting) {\n state.pendingExtraction = true\n return\n }\n\n state.isExtracting = true\n state.pendingExtraction = false\n\n try {\n await onExtract()\n } finally {\n state.isExtracting = false\n\n // If a change came in during extraction, run again\n if (state.pendingExtraction) {\n state.pendingExtraction = false\n await runExtraction()\n }\n }\n }\n\n return {runExtraction, state}\n}\n\n/**\n * Starts a schema watcher that extracts schema on file changes.\n * Returns a watcher instance and a stop function.\n */\nexport async function startExtractSchemaWatcher(\n options: ExtractSchemaWatcherOptions,\n): Promise<ExtractSchemaWatcher> {\n const {extractOptions, onExtraction, output, watchPatterns} = options\n\n const {configPath, enforceRequiredFields, outputPath} = extractOptions\n const workDir = dirname(configPath)\n\n // Helper function to run extraction with spinner and error handling\n const runExtraction = async (): Promise<boolean> => {\n const spin = spinner(\n enforceRequiredFields\n ? 'Extracting schema with enforced required fields'\n : 'Extracting schema...',\n ).start()\n const extractionStartTime = Date.now()\n\n try {\n await runSchemaExtraction(extractOptions)\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: true})\n\n return true\n } catch (err) {\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: false})\n\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail('Extraction failed')\n\n // Display validation errors if available\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n } else if (err instanceof Error) {\n output.error(err.message, {exit: 1})\n }\n\n return false\n }\n }\n\n // Run initial extraction\n await runExtraction()\n\n const absoluteWatchPatterns = watchPatterns.map((pattern) => join(workDir, pattern))\n\n // Create extraction runner with concurrency control\n const {runExtraction: runConcurrentExtraction} = createExtractionRunner(async () => {\n await runExtraction()\n })\n\n // Debounced extraction trigger (1 second delay)\n const debouncedExtract = debounce(() => {\n void runConcurrentExtraction()\n }, 1000)\n\n const {isMatch} = createSchemaPatternMatcher(watchPatterns)\n\n const watcher: FSWatcher = chokidarWatch(absoluteWatchPatterns, {\n cwd: workDir,\n ignored: IGNORED_PATTERNS,\n ignoreInitial: true,\n })\n\n watcher.on('all', (event, filePath) => {\n if (!isMatch(filePath, workDir)) {\n return\n }\n\n const timestamp = new Date().toLocaleTimeString()\n const relativePath = isAbsolute(filePath) ? relative(workDir, filePath) : filePath\n output.log(`[${timestamp}] ${event}: ${relativePath}`)\n debouncedExtract()\n })\n\n watcher.on('error', (err) => {\n output.error(`Watcher error: ${err instanceof Error ? err.message : String(err)}`)\n })\n\n return {\n close: () => watcher.close(),\n watcher,\n }\n}\n"],"names":["dirname","isAbsolute","join","relative","spinner","watch","chokidarWatch","debounce","formatSchemaValidation","createSchemaPatternMatcher","runSchemaExtraction","schemasExtractDebug","SchemaExtractionError","DEFAULT_WATCH_PATTERNS","IGNORED_PATTERNS","createExtractionRunner","onExtract","state","isExtracting","pendingExtraction","runExtraction","startExtractSchemaWatcher","options","extractOptions","onExtraction","output","watchPatterns","configPath","enforceRequiredFields","outputPath","workDir","spin","start","extractionStartTime","Date","now","succeed","duration","success","err","fail","validation","length","log","Error","error","message","exit","absoluteWatchPatterns","map","pattern","runConcurrentExtraction","debouncedExtract","isMatch","watcher","cwd","ignored","ignoreInitial","on","event","filePath","timestamp","toLocaleTimeString","relativePath","String","close"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/schema/extractSchemaWatcher.ts"],"sourcesContent":["import {dirname, isAbsolute, join, relative} from 'node:path'\n\nimport {\n createSchemaPatternMatcher,\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {watch as chokidarWatch, type FSWatcher} from 'chokidar'\nimport debounce from 'lodash-es/debounce.js'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\n/** Default glob patterns to watch for schema changes */\nexport const DEFAULT_WATCH_PATTERNS = [\n 'sanity.config.{js,jsx,ts,tsx,mjs}',\n 'schema*/**/*.{js,jsx,ts,tsx,mjs}',\n]\n\n/** Default patterns to ignore when watching */\nconst IGNORED_PATTERNS = [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/lib/**',\n '**/.sanity/**',\n]\n\ninterface ExtractSchemaWatcherOptions {\n extractOptions: ExtractOptions\n output: Output\n watchPatterns: string[]\n\n onExtraction?: (result: {duration: number; success: boolean}) => void\n}\n\ninterface ExtractSchemaWatcher {\n close: () => Promise<void>\n watcher: FSWatcher\n}\n\n/** State for tracking extraction status */\ninterface WatchState {\n isExtracting: boolean\n pendingExtraction: boolean\n}\n\n/** Return type for createExtractionRunner */\ninterface ExtractionRunner {\n runExtraction: () => Promise<void>\n state: WatchState\n}\n\n/**\n * Creates an extraction runner with concurrency control.\n * If extraction is already running, queues one more extraction to run after completion.\n * Multiple queued requests are coalesced into a single pending extraction.\n */\nfunction createExtractionRunner(onExtract: () => Promise<void>): ExtractionRunner {\n const state: WatchState = {\n isExtracting: false,\n pendingExtraction: false,\n }\n\n async function runExtraction(): Promise<void> {\n if (state.isExtracting) {\n state.pendingExtraction = true\n return\n }\n\n state.isExtracting = true\n state.pendingExtraction = false\n\n try {\n await onExtract()\n } finally {\n state.isExtracting = false\n\n // If a change came in during extraction, run again\n if (state.pendingExtraction) {\n state.pendingExtraction = false\n await runExtraction()\n }\n }\n }\n\n return {runExtraction, state}\n}\n\n/**\n * Starts a schema watcher that extracts schema on file changes.\n * Returns a watcher instance and a stop function.\n */\nexport async function startExtractSchemaWatcher(\n options: ExtractSchemaWatcherOptions,\n): Promise<ExtractSchemaWatcher> {\n const {extractOptions, onExtraction, output, watchPatterns} = options\n\n const {configPath, enforceRequiredFields, outputPath} = extractOptions\n const workDir = dirname(configPath)\n\n // Helper function to run extraction with spinner and error handling\n const runExtraction = async (): Promise<boolean> => {\n const spin = spinner(\n enforceRequiredFields\n ? 'Extracting schema with enforced required fields'\n : 'Extracting schema...',\n ).start()\n const extractionStartTime = Date.now()\n\n try {\n await runSchemaExtraction(extractOptions)\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: true})\n\n return true\n } catch (err) {\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: false})\n\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail('Extraction failed')\n\n // Display validation errors if available\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n } else if (err instanceof Error) {\n output.error(err.message, {exit: 1})\n }\n\n return false\n }\n }\n\n // Run initial extraction\n await runExtraction()\n\n const absoluteWatchPatterns = watchPatterns.map((pattern) => join(workDir, pattern))\n\n // Create extraction runner with concurrency control\n const {runExtraction: runConcurrentExtraction} = createExtractionRunner(async () => {\n await runExtraction()\n })\n\n // Debounced extraction trigger (1 second delay)\n const debouncedExtract = debounce(() => {\n void runConcurrentExtraction()\n }, 1000)\n\n const {isMatch} = createSchemaPatternMatcher(watchPatterns)\n\n const watcher: FSWatcher = chokidarWatch(absoluteWatchPatterns, {\n cwd: workDir,\n ignored: IGNORED_PATTERNS,\n ignoreInitial: true,\n })\n\n watcher.on('all', (event, filePath) => {\n if (!isMatch(filePath, workDir)) {\n return\n }\n\n const timestamp = new Date().toLocaleTimeString()\n const relativePath = isAbsolute(filePath) ? relative(workDir, filePath) : filePath\n output.log(`[${timestamp}] ${event}: ${relativePath}`)\n debouncedExtract()\n })\n\n watcher.on('error', (err) => {\n output.error(`Watcher error: ${err instanceof Error ? err.message : String(err)}`)\n })\n\n return {\n close: () => watcher.close(),\n watcher,\n }\n}\n"],"names":["dirname","isAbsolute","join","relative","createSchemaPatternMatcher","formatSchemaValidation","runSchemaExtraction","SchemaExtractionError","spinner","watch","chokidarWatch","debounce","schemasExtractDebug","DEFAULT_WATCH_PATTERNS","IGNORED_PATTERNS","createExtractionRunner","onExtract","state","isExtracting","pendingExtraction","runExtraction","startExtractSchemaWatcher","options","extractOptions","onExtraction","output","watchPatterns","configPath","enforceRequiredFields","outputPath","workDir","spin","start","extractionStartTime","Date","now","succeed","duration","success","err","fail","validation","length","log","Error","error","message","exit","absoluteWatchPatterns","map","pattern","runConcurrentExtraction","debouncedExtract","isMatch","watcher","cwd","ignored","ignoreInitial","on","event","filePath","timestamp","toLocaleTimeString","relativePath","String","close"],"mappings":"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"}
@@ -1,23 +1,13 @@
1
- import { existsSync, statSync } from 'node:fs';
2
- import { extname, join, resolve } from 'node:path';
1
+ import { getExtractOptions as internalExtractOptions } from '@sanity/cli-build/_internal/extract';
3
2
  export function getExtractOptions({ flags, projectRoot, schemaExtraction }) {
4
- const pathFlag = flags.path ?? schemaExtraction?.path;
5
- let outputPath;
6
- if (pathFlag) {
7
- const resolved = resolve(join(projectRoot.directory, pathFlag));
8
- const isExistingDirectory = existsSync(resolved) && statSync(resolved).isDirectory();
9
- outputPath = isExistingDirectory || !extname(resolved) ? join(resolved, 'schema.json') : resolved;
10
- } else {
11
- outputPath = resolve(join(projectRoot.directory, 'schema.json'));
12
- }
13
- return {
14
- configPath: projectRoot.path,
15
- enforceRequiredFields: flags['enforce-required-fields'] ?? schemaExtraction?.enforceRequiredFields ?? false,
16
- format: flags.format ?? 'groq-type-nodes',
17
- outputPath,
18
- watchPatterns: flags['watch-patterns'] ?? schemaExtraction?.watchPatterns ?? [],
3
+ return internalExtractOptions({
4
+ enforceRequiredFields: flags['enforce-required-fields'] ?? schemaExtraction?.enforceRequiredFields,
5
+ format: flags.format,
6
+ path: flags.path ?? schemaExtraction?.path,
7
+ projectRoot: projectRoot,
8
+ watchPatterns: flags['watch-patterns'] ?? schemaExtraction?.watchPatterns,
19
9
  workspace: flags.workspace ?? schemaExtraction?.workspace
20
- };
10
+ });
21
11
  }
22
12
 
23
13
  //# sourceMappingURL=getExtractOptions.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/getExtractOptions.ts"],"sourcesContent":["import {existsSync, statSync} from 'node:fs'\nimport {extname, join, resolve} from 'node:path'\n\nimport {type CliConfig, ProjectRootResult} from '@sanity/cli-core'\n\nimport {type ExtractSchemaCommand} from '../../commands/schemas/extract.js'\n\nexport interface ExtractOptions {\n configPath: string\n enforceRequiredFields: boolean\n format: string\n outputPath: string\n watchPatterns: string[]\n workspace: string | undefined\n}\n\ninterface GetExtractionOptions {\n flags: ExtractSchemaCommand['flags']\n projectRoot: ProjectRootResult\n schemaExtraction: CliConfig['schemaExtraction']\n}\n\nexport function getExtractOptions({\n flags,\n projectRoot,\n schemaExtraction,\n}: GetExtractionOptions): ExtractOptions {\n const pathFlag = flags.path ?? schemaExtraction?.path\n let outputPath: string\n if (pathFlag) {\n const resolved = resolve(join(projectRoot.directory, pathFlag))\n const isExistingDirectory = existsSync(resolved) && statSync(resolved).isDirectory()\n\n outputPath =\n isExistingDirectory || !extname(resolved) ? join(resolved, 'schema.json') : resolved\n } else {\n outputPath = resolve(join(projectRoot.directory, 'schema.json'))\n }\n\n return {\n configPath: projectRoot.path,\n enforceRequiredFields:\n flags['enforce-required-fields'] ?? schemaExtraction?.enforceRequiredFields ?? false,\n format: flags.format ?? 'groq-type-nodes',\n outputPath,\n watchPatterns: flags['watch-patterns'] ?? schemaExtraction?.watchPatterns ?? [],\n workspace: flags.workspace ?? schemaExtraction?.workspace,\n }\n}\n"],"names":["existsSync","statSync","extname","join","resolve","getExtractOptions","flags","projectRoot","schemaExtraction","pathFlag","path","outputPath","resolved","directory","isExistingDirectory","isDirectory","configPath","enforceRequiredFields","format","watchPatterns","workspace"],"mappings":"AAAA,SAAQA,UAAU,EAAEC,QAAQ,QAAO,UAAS;AAC5C,SAAQC,OAAO,EAAEC,IAAI,EAAEC,OAAO,QAAO,YAAW;AAqBhD,OAAO,SAASC,kBAAkB,EAChCC,KAAK,EACLC,WAAW,EACXC,gBAAgB,EACK;IACrB,MAAMC,WAAWH,MAAMI,IAAI,IAAIF,kBAAkBE;IACjD,IAAIC;IACJ,IAAIF,UAAU;QACZ,MAAMG,WAAWR,QAAQD,KAAKI,YAAYM,SAAS,EAAEJ;QACrD,MAAMK,sBAAsBd,WAAWY,aAAaX,SAASW,UAAUG,WAAW;QAElFJ,aACEG,uBAAuB,CAACZ,QAAQU,YAAYT,KAAKS,UAAU,iBAAiBA;IAChF,OAAO;QACLD,aAAaP,QAAQD,KAAKI,YAAYM,SAAS,EAAE;IACnD;IAEA,OAAO;QACLG,YAAYT,YAAYG,IAAI;QAC5BO,uBACEX,KAAK,CAAC,0BAA0B,IAAIE,kBAAkBS,yBAAyB;QACjFC,QAAQZ,MAAMY,MAAM,IAAI;QACxBP;QACAQ,eAAeb,KAAK,CAAC,iBAAiB,IAAIE,kBAAkBW,iBAAiB,EAAE;QAC/EC,WAAWd,MAAMc,SAAS,IAAIZ,kBAAkBY;IAClD;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/schema/getExtractOptions.ts"],"sourcesContent":["import {\n type ExtractOptions,\n getExtractOptions as internalExtractOptions,\n} from '@sanity/cli-build/_internal/extract'\nimport {type CliConfig, type ProjectRootResult} from '@sanity/cli-core'\n\nimport {type ExtractSchemaCommand} from '../../commands/schemas/extract.js'\n\ninterface GetExtractionOptions {\n flags: ExtractSchemaCommand['flags']\n projectRoot: ProjectRootResult\n schemaExtraction: CliConfig['schemaExtraction']\n}\n\nexport function getExtractOptions({\n flags,\n projectRoot,\n schemaExtraction,\n}: GetExtractionOptions): ExtractOptions {\n return internalExtractOptions({\n enforceRequiredFields:\n flags['enforce-required-fields'] ?? schemaExtraction?.enforceRequiredFields,\n format: flags.format,\n path: flags.path ?? schemaExtraction?.path,\n projectRoot: projectRoot,\n watchPatterns: flags['watch-patterns'] ?? schemaExtraction?.watchPatterns,\n workspace: flags.workspace ?? schemaExtraction?.workspace,\n })\n}\n"],"names":["getExtractOptions","internalExtractOptions","flags","projectRoot","schemaExtraction","enforceRequiredFields","format","path","watchPatterns","workspace"],"mappings":"AAAA,SAEEA,qBAAqBC,sBAAsB,QACtC,sCAAqC;AAW5C,OAAO,SAASD,kBAAkB,EAChCE,KAAK,EACLC,WAAW,EACXC,gBAAgB,EACK;IACrB,OAAOH,uBAAuB;QAC5BI,uBACEH,KAAK,CAAC,0BAA0B,IAAIE,kBAAkBC;QACxDC,QAAQJ,MAAMI,MAAM;QACpBC,MAAML,MAAMK,IAAI,IAAIH,kBAAkBG;QACtCJ,aAAaA;QACbK,eAAeN,KAAK,CAAC,iBAAiB,IAAIE,kBAAkBI;QAC5DC,WAAWP,MAAMO,SAAS,IAAIL,kBAAkBK;IAClD;AACF"}
@@ -1,10 +1,4 @@
1
1
  import { z } from 'zod/mini';
2
- export const extractSchemaWorkerData = z.object({
3
- configPath: z.string(),
4
- enforceRequiredFields: z.boolean(),
5
- workDir: z.string(),
6
- workspaceName: z.optional(z.string())
7
- });
8
2
  export const uniqWorkspaceWorkerDataSchema = z.object({
9
3
  configPath: z.string(),
10
4
  dataset: z.optional(z.string())