@sanity/cli 3.85.2-media-library.14 → 3.86.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/cli",
3
- "version": "3.85.2-media-library.14+fedc36ab36",
3
+ "version": "3.86.0",
4
4
  "description": "Sanity CLI tool for managing Sanity installations, managing plugins, schemas and datasets",
5
5
  "keywords": [
6
6
  "sanity",
@@ -59,11 +59,11 @@
59
59
  "dependencies": {
60
60
  "@babel/traverse": "^7.23.5",
61
61
  "@sanity/client": "^6.29.0",
62
- "@sanity/codegen": "3.85.2-media-library.14+fedc36ab36",
63
- "@sanity/runtime-cli": "^2.5.0",
62
+ "@sanity/codegen": "3.86.0",
63
+ "@sanity/runtime-cli": "^3.0.0",
64
64
  "@sanity/telemetry": "^0.8.0",
65
65
  "@sanity/template-validator": "^2.4.3",
66
- "@sanity/util": "3.85.2-media-library.14+fedc36ab36",
66
+ "@sanity/util": "3.86.0",
67
67
  "chalk": "^4.1.2",
68
68
  "debug": "^4.3.4",
69
69
  "decompress": "^4.2.0",
@@ -83,7 +83,7 @@
83
83
  "@rollup/plugin-node-resolve": "^15.2.3",
84
84
  "@sanity/eslint-config-studio": "^4.0.0",
85
85
  "@sanity/generate-help-url": "^3.0.0",
86
- "@sanity/types": "3.85.2-media-library.14+fedc36ab36",
86
+ "@sanity/types": "3.86.0",
87
87
  "@types/babel__traverse": "^7.20.5",
88
88
  "@types/configstore": "^5.0.1",
89
89
  "@types/cpx": "^1.5.2",
@@ -135,5 +135,5 @@
135
135
  "engines": {
136
136
  "node": ">=18"
137
137
  },
138
- "gitHead": "fedc36ab3627655fb7be4021ba6e22aaa2641b14"
138
+ "gitHead": "29b5941dceac3ece48124c4ad586aa7431f33043"
139
139
  }
@@ -0,0 +1,81 @@
1
+ import {type CliCommandDefinition} from '../../types'
2
+
3
+ const helpText = `
4
+ Arguments
5
+ [type] Type of Resource to add (currently only 'function' is supported)
6
+
7
+ Examples
8
+ # Add a Function Resource
9
+ sanity blueprints add function
10
+ `
11
+
12
+ const addBlueprintsCommand: CliCommandDefinition = {
13
+ name: 'add',
14
+ group: 'blueprints',
15
+ helpText,
16
+ signature: '<type>',
17
+ description: 'Add a Resource to a Blueprint',
18
+ hideFromHelp: true,
19
+ async action(args, context) {
20
+ const {output, prompt} = context
21
+ const {print} = output
22
+
23
+ const [resourceType] = args.argsWithoutOptions
24
+
25
+ if (!resourceType) {
26
+ print('Resource type is required. Available types: function')
27
+ return
28
+ }
29
+
30
+ const {blueprint: blueprintAction, resources: resourcesAction} = await import(
31
+ '@sanity/runtime-cli/actions/blueprints'
32
+ )
33
+
34
+ const existingBlueprint = blueprintAction.findBlueprintFile()
35
+ if (!existingBlueprint) {
36
+ print('No blueprint file found. Run `sanity blueprints init` first.')
37
+ return
38
+ }
39
+
40
+ if (resourceType === 'function') {
41
+ const functionName = await prompt.single({
42
+ type: 'input',
43
+ message: 'Enter function name:',
44
+ validate: (input: string) => input.length > 0 || 'Function name is required',
45
+ })
46
+
47
+ const functionType = await prompt.single({
48
+ type: 'list',
49
+ message: 'Choose function type:',
50
+ choices: [
51
+ {value: 'document-publish', name: 'Document Publish'},
52
+ {value: 'document-create', name: 'Document Create (Available soon)', disabled: true},
53
+ {value: 'document-delete', name: 'Document Delete (Available soon)', disabled: true},
54
+ ],
55
+ })
56
+
57
+ const {filePath, resourceAdded, resource} = resourcesAction.createFunctionResource({
58
+ name: functionName,
59
+ type: functionType,
60
+ displayName: functionName,
61
+ })
62
+
63
+ print(`\nCreated function: ${filePath}`)
64
+
65
+ if (resourceAdded) {
66
+ // added to blueprint.json
67
+ print('Function Resource added to Blueprint')
68
+ } else {
69
+ // print the resource JSON for manual addition
70
+ print('\nAdd this Function Resource to your Blueprint:')
71
+ print(JSON.stringify(resource, null, 2))
72
+ }
73
+
74
+ return
75
+ }
76
+
77
+ print(`Unsupported resource type: ${resourceType}. Available types: function`)
78
+ },
79
+ }
80
+
81
+ export default addBlueprintsCommand
@@ -0,0 +1,11 @@
1
+ import {type CliCommandGroupDefinition} from '../../types'
2
+
3
+ const blueprintsGroup: CliCommandGroupDefinition = {
4
+ name: 'blueprints',
5
+ signature: '[COMMAND]',
6
+ isGroupRoot: true,
7
+ description: 'Deploy and manage Sanity Blueprints and Stacks (IaC)',
8
+ hideFromHelp: true,
9
+ }
10
+
11
+ export default blueprintsGroup
@@ -0,0 +1,112 @@
1
+ import {type CliCommandDefinition} from '../../types'
2
+
3
+ const helpText = `
4
+ Options
5
+ --edit Edit the configuration
6
+
7
+ Examples
8
+ # View current configuration
9
+ sanity blueprints config
10
+
11
+ # Edit configuration
12
+ sanity blueprints config --edit
13
+ `
14
+
15
+ const defaultFlags = {
16
+ edit: false,
17
+ }
18
+
19
+ const configBlueprintsCommand: CliCommandDefinition = {
20
+ name: 'config',
21
+ group: 'blueprints',
22
+ helpText,
23
+ signature: '[--edit]',
24
+ description: 'View or edit local Blueprints configuration',
25
+ hideFromHelp: true,
26
+ async action(args, context) {
27
+ const {apiClient, output, prompt} = context
28
+ const {print} = output
29
+ const flags = {...defaultFlags, ...args.extOptions}
30
+
31
+ const client = apiClient({
32
+ requireUser: true,
33
+ requireProject: false,
34
+ })
35
+ const {token} = client.config()
36
+ const {
37
+ blueprint: blueprintAction,
38
+ projects: projectsAction,
39
+ stacks: stacksAction,
40
+ } = await import('@sanity/runtime-cli/actions/blueprints')
41
+
42
+ const config = blueprintAction.readConfigFile()
43
+ if (!config) {
44
+ print('No configuration found. Run `sanity blueprints init` first.')
45
+ return
46
+ }
47
+
48
+ print('\nCurrent configuration:')
49
+ print(JSON.stringify(config, null, 2))
50
+
51
+ if (!flags.edit) {
52
+ return
53
+ }
54
+
55
+ if (!token) {
56
+ print('No API token found. Please run `sanity login` first.')
57
+ return
58
+ }
59
+
60
+ const {ok, projects, error} = await projectsAction.listProjects({token})
61
+ if (!ok) {
62
+ print(error)
63
+ return
64
+ }
65
+
66
+ if (!projects || projects.length === 0) {
67
+ print('No Projects found. Please create a Project in Sanity.io first.')
68
+ return
69
+ }
70
+
71
+ const projectChoices = projects.map(({displayName, id}) => ({
72
+ value: id,
73
+ name: `${displayName} <${id}>`,
74
+ }))
75
+
76
+ const projectId = await prompt.single({
77
+ type: 'list',
78
+ message: 'Select your Sanity Project:',
79
+ choices: projectChoices,
80
+ default: config.projectId,
81
+ })
82
+
83
+ const auth = {token, projectId}
84
+
85
+ // get stacks for selected project
86
+ const {ok: stacksOk, stacks, error: stacksError} = await stacksAction.listStacks(auth)
87
+ if (!stacksOk) {
88
+ print(stacksError)
89
+ return
90
+ }
91
+
92
+ let stackId
93
+ if (stacks && stacks.length > 0) {
94
+ const stackChoices = stacks.map(({name, id}) => ({
95
+ value: id,
96
+ name: `${name} <${id}>`,
97
+ }))
98
+
99
+ stackId = await prompt.single({
100
+ type: 'list',
101
+ message: 'Select a Stack:',
102
+ choices: stackChoices,
103
+ default: config.stackId,
104
+ })
105
+ }
106
+
107
+ blueprintAction.writeConfigFile({projectId, stackId})
108
+ print('\nConfiguration updated successfully.')
109
+ },
110
+ }
111
+
112
+ export default configBlueprintsCommand
@@ -0,0 +1,164 @@
1
+ import {type CliCommandDefinition} from '../../types'
2
+
3
+ const helpText = `
4
+ Examples
5
+ # Deploy the current blueprint
6
+ sanity blueprints deploy
7
+ `
8
+
9
+ const deployBlueprintsCommand: CliCommandDefinition = {
10
+ name: 'deploy',
11
+ group: 'blueprints',
12
+ helpText,
13
+ signature: '',
14
+ description: 'Deploy a Blueprint to create or update a Stack',
15
+ hideFromHelp: true,
16
+ /* eslint-disable-next-line complexity, max-statements */
17
+ async action(args, context) {
18
+ const {apiClient, output, prompt} = context
19
+ const {print} = output
20
+
21
+ const client = apiClient({
22
+ requireUser: true,
23
+ requireProject: false,
24
+ })
25
+ const {token} = client.config()
26
+ const {
27
+ blueprint: blueprintAction,
28
+ stacks: stacksAction,
29
+ assets: assetsAction,
30
+ } = await import('@sanity/runtime-cli/actions/blueprints')
31
+ const {display} = await import('@sanity/runtime-cli/utils')
32
+
33
+ if (!token) {
34
+ print('No API token found. Please run `sanity login` first.')
35
+ return
36
+ }
37
+
38
+ let blueprint = null
39
+ try {
40
+ blueprint = await blueprintAction.readBlueprintOnDisk({getStack: true, token})
41
+ } catch (error) {
42
+ print('Unable to read Blueprint manifest file. Run `sanity blueprints init`')
43
+ return
44
+ }
45
+
46
+ if (!blueprint) {
47
+ print('Unable to read Blueprint manifest file. Run `sanity blueprints init`')
48
+ return
49
+ }
50
+
51
+ const {
52
+ errors,
53
+ projectId: configuredProjectId,
54
+ stackId,
55
+ parsedBlueprint,
56
+ deployedStack,
57
+ } = blueprint
58
+
59
+ if (errors && errors.length > 0) {
60
+ print(errors)
61
+ return
62
+ }
63
+
64
+ if (stackId && !deployedStack) {
65
+ print('Stack specified in config, but deployed Stack not found.')
66
+ return
67
+ }
68
+
69
+ const resources = parsedBlueprint.resources || []
70
+
71
+ let projectId = configuredProjectId
72
+ if (!projectId) {
73
+ print('No Sanity Project context found in Blueprint configuration.')
74
+
75
+ const maybeProjectId = await prompt.single({
76
+ type: 'input',
77
+ message: 'Enter Sanity Project ID:',
78
+ })
79
+
80
+ projectId = maybeProjectId
81
+ }
82
+
83
+ if (!projectId) {
84
+ print('Sanity Project context is required')
85
+ print('To configure this Blueprint, run `sanity blueprints config`')
86
+ return
87
+ }
88
+
89
+ const auth = {token, projectId}
90
+
91
+ let name = deployedStack?.name
92
+ if (!name) {
93
+ const stackName = await prompt.single({
94
+ type: 'input',
95
+ message: 'Enter stack name:',
96
+ validate: (input) => input.length > 0 || 'Stack name is required',
97
+ })
98
+
99
+ name = stackName
100
+ }
101
+
102
+ if (!name) {
103
+ print('Stack name is required')
104
+ return
105
+ }
106
+
107
+ const validResources = resources?.filter((r) => r.type)
108
+ const functionResources = validResources?.filter((r) => r.type.startsWith('sanity.function.'))
109
+
110
+ if (functionResources?.length) {
111
+ for (const resource of functionResources) {
112
+ print(`Processing ${resource.name}...`)
113
+ const result = await assetsAction.stashAsset({resource, auth})
114
+
115
+ if (result.success && result.assetId) {
116
+ const src = resource.src
117
+ resource.src = result.assetId // ! this will change! for now, the API expects the assetId
118
+ const {yellow} = display.colors
119
+ print(`${resource.name} <${yellow(result.assetId)}>`)
120
+ print(` Source: ${src}`)
121
+ } else {
122
+ print(` Error: ${result.error}`)
123
+ throw new Error(`Failed to process ${resource.name}`)
124
+ }
125
+ }
126
+ }
127
+
128
+ const stackPayload = {
129
+ name,
130
+ projectId,
131
+ document: {resources: validResources},
132
+ }
133
+
134
+ print('Deploying stack...')
135
+
136
+ const {
137
+ ok: deployOk,
138
+ stack,
139
+ error: deployError,
140
+ } = deployedStack
141
+ ? await stacksAction.updateStack({stackId: deployedStack.id, stackPayload, auth})
142
+ : await stacksAction.createStack({stackPayload, auth})
143
+
144
+ if (deployOk) {
145
+ const {green, bold, yellow} = display.colors
146
+ print(
147
+ `${green('Success!')} Stack "${bold(stack.name)}" ${deployedStack ? 'updated' : 'created'} <${yellow(stack.id)}>`,
148
+ )
149
+
150
+ blueprintAction.writeConfigFile({
151
+ projectId,
152
+ stackId: stack.id,
153
+ })
154
+
155
+ print('\nUse `sanity blueprints info` to check deployment status')
156
+ } else {
157
+ const {red} = display.colors
158
+ print(`${red('Failed')} to ${deployedStack ? 'update' : 'create'} stack`)
159
+ print(`Error: ${deployError || JSON.stringify(stack, null, 2) || 'Unknown error'}`)
160
+ }
161
+ },
162
+ }
163
+
164
+ export default deployBlueprintsCommand
@@ -0,0 +1,84 @@
1
+ import {type CliCommandDefinition} from '../../types'
2
+
3
+ const helpText = `
4
+ Options
5
+ --id, -i Stack ID
6
+
7
+ Examples
8
+ # Retrieve information about the current Stack
9
+ sanity blueprints info
10
+
11
+ # Retrieve information about a specific Stack
12
+ sanity blueprints info --id <stack-id>
13
+ `
14
+
15
+ const defaultFlags = {id: undefined}
16
+
17
+ const infoBlueprintsCommand: CliCommandDefinition = {
18
+ name: 'info',
19
+ group: 'blueprints',
20
+ helpText,
21
+ signature: '',
22
+ description: 'Retrieve information about a Blueprint Stack',
23
+ hideFromHelp: true,
24
+ async action(args, context) {
25
+ const {apiClient, output} = context
26
+ const {print} = output
27
+ const flags = {...defaultFlags, ...args.extOptions}
28
+
29
+ const client = apiClient({
30
+ requireUser: true,
31
+ requireProject: false,
32
+ })
33
+ const {token} = client.config()
34
+ const {blueprint: blueprintAction, stacks: stacksAction} = await import(
35
+ '@sanity/runtime-cli/actions/blueprints'
36
+ )
37
+ const {display} = await import('@sanity/runtime-cli/utils')
38
+
39
+ let blueprint = null
40
+ try {
41
+ blueprint = await blueprintAction.readBlueprintOnDisk({token})
42
+ } catch (error) {
43
+ print('Unable to read Blueprint manifest file. Run `sanity blueprints init`')
44
+ return
45
+ }
46
+
47
+ if (!blueprint) {
48
+ print('Unable to read Blueprint manifest file. Run `sanity blueprints init`')
49
+ return
50
+ }
51
+
52
+ const {errors, deployedStack, projectId} = blueprint
53
+
54
+ if (errors && errors.length > 0) {
55
+ print(errors)
56
+ return
57
+ }
58
+
59
+ if (token && projectId) {
60
+ const auth = {token, projectId}
61
+ let result
62
+ if (flags.id) {
63
+ const {stack, error, ok} = await stacksAction.getStack({stackId: flags.id, auth})
64
+ if (ok) {
65
+ result = stack
66
+ } else {
67
+ print(error)
68
+ }
69
+ } else {
70
+ result = deployedStack
71
+ }
72
+
73
+ if (result) {
74
+ print(display.blueprintsFormatting.formatStackInfo(result))
75
+ } else {
76
+ print('No Stack found')
77
+ }
78
+ } else {
79
+ print('Cannot retrieve information for Blueprint: missing API token or Project ID')
80
+ }
81
+ },
82
+ }
83
+
84
+ export default infoBlueprintsCommand
@@ -0,0 +1,96 @@
1
+ import {join} from 'node:path'
2
+ import {cwd} from 'node:process'
3
+
4
+ import {type CliCommandDefinition} from '../../types'
5
+
6
+ const helpText = `
7
+ Examples
8
+ # Create a new Blueprint manifest file
9
+ sanity blueprints init
10
+ `
11
+
12
+ const initBlueprintsCommand: CliCommandDefinition = {
13
+ name: 'init',
14
+ group: 'blueprints',
15
+ helpText,
16
+ signature: '',
17
+ description: 'Initialize a new Blueprint manifest file',
18
+ hideFromHelp: true,
19
+ async action(args, context) {
20
+ const {apiClient, output, prompt} = context
21
+ const {print} = output
22
+
23
+ const client = apiClient({
24
+ requireUser: true,
25
+ requireProject: false,
26
+ })
27
+ const {token} = client.config()
28
+
29
+ if (!token) {
30
+ print('No API token found. Please run `sanity login` first.')
31
+ return
32
+ }
33
+
34
+ const {blueprint: blueprintAction, projects: projectsAction} = await import(
35
+ '@sanity/runtime-cli/actions/blueprints'
36
+ )
37
+
38
+ const existingBlueprint = blueprintAction.findBlueprintFile()
39
+
40
+ if (existingBlueprint) {
41
+ print(`A blueprint file already exists: ${existingBlueprint.fileName}`)
42
+ return
43
+ }
44
+
45
+ const blueprintExtension = await prompt.single({
46
+ type: 'list',
47
+ message: 'Choose a Blueprint manifest file type:',
48
+ choices: [
49
+ {value: 'json', name: 'JSON (Recommended)'},
50
+ {value: 'js', name: 'JavaScript (Available soon)', disabled: true},
51
+ {value: 'ts', name: 'TypeScript (Available soon)', disabled: true},
52
+ ],
53
+ })
54
+
55
+ const {ok, projects, error} = await projectsAction.listProjects({token})
56
+
57
+ if (!ok) {
58
+ print(error)
59
+ return
60
+ }
61
+
62
+ if (!projects || projects.length === 0) {
63
+ print('No Projects found. Please create a Project in Sanity.io first.')
64
+ return
65
+ }
66
+
67
+ const projectChoices = projects.map(({displayName, id}) => ({
68
+ value: id,
69
+ name: `${displayName} <${id}>`,
70
+ }))
71
+
72
+ const projectId = await prompt.single({
73
+ type: 'list',
74
+ message: 'Select your Sanity Project:',
75
+ choices: projectChoices,
76
+ })
77
+
78
+ const fileName = `blueprint.${blueprintExtension}`
79
+ const filePath = join(cwd(), fileName)
80
+
81
+ blueprintAction.writeBlueprintToDisk({
82
+ path: filePath,
83
+ fileType: blueprintExtension as 'json' | 'js' | 'ts',
84
+ })
85
+
86
+ blueprintAction.writeConfigFile({projectId})
87
+
88
+ print(`Created new blueprint: ./${fileName}`)
89
+
90
+ if (blueprintExtension === 'ts') {
91
+ print('\nNote: TypeScript support requires "tsx" to be installed. Run: npm install -D tsx')
92
+ }
93
+ },
94
+ }
95
+
96
+ export default initBlueprintsCommand
@@ -0,0 +1,111 @@
1
+ import {type CliCommandDefinition} from '../../types'
2
+
3
+ const helpText = `
4
+ ${
5
+ /*Options
6
+ --watch, -w Watch for new logs (streaming mode)
7
+ */ ''
8
+ }
9
+ Examples
10
+ # Show logs for the current Stack
11
+ sanity blueprints logs
12
+ ${
13
+ /*
14
+ # Watch for new logs
15
+ sanity blueprints logs --watch
16
+ */ ''
17
+ }
18
+ `
19
+
20
+ // const defaultFlags = {watch: false}
21
+
22
+ const logsBlueprintsCommand: CliCommandDefinition = {
23
+ name: 'logs',
24
+ group: 'blueprints',
25
+ helpText,
26
+ signature: '[--watch]',
27
+ description: 'Display logs for the current Blueprint Stack',
28
+ hideFromHelp: true,
29
+ async action(args, context) {
30
+ const {apiClient, output} = context
31
+ const {print} = output
32
+ // const flags = {...defaultFlags, ...args.extOptions}
33
+ // const watchMode = Boolean(flags.watch)
34
+
35
+ const client = apiClient({requireUser: true, requireProject: false})
36
+ const {token} = client.config()
37
+
38
+ if (!token) {
39
+ print('No API token found. Please run `sanity login` first.')
40
+ return
41
+ }
42
+
43
+ const {blueprint: blueprintAction, logs: logsAction} = await import(
44
+ '@sanity/runtime-cli/actions/blueprints'
45
+ )
46
+ const {display} = await import('@sanity/runtime-cli/utils')
47
+
48
+ let blueprint = null
49
+ try {
50
+ blueprint = await blueprintAction.readBlueprintOnDisk({token})
51
+ } catch (error) {
52
+ print('Unable to read Blueprint manifest file. Run `sanity blueprints init`')
53
+ return
54
+ }
55
+
56
+ if (!blueprint) {
57
+ print('Unable to read Blueprint manifest file. Run `sanity blueprints init`')
58
+ return
59
+ }
60
+
61
+ const {errors, deployedStack} = blueprint
62
+
63
+ if (errors && errors.length > 0) {
64
+ print(errors)
65
+ return
66
+ }
67
+
68
+ if (!deployedStack) {
69
+ print('Stack not found')
70
+ return
71
+ }
72
+
73
+ const {id: stackId, projectId, name} = deployedStack
74
+ const auth = {token, projectId}
75
+
76
+ print(`Fetching logs for stack ${display.colors.yellow(`<${stackId}>`)}`)
77
+
78
+ // enable watch mode here
79
+
80
+ try {
81
+ const {ok, logs, error} = await logsAction.getLogs(stackId, auth)
82
+
83
+ if (!ok) {
84
+ print(`${display.colors.red('Failed')} to retrieve logs`)
85
+ print(`Error: ${error || 'Unknown error'}`)
86
+ return
87
+ }
88
+
89
+ if (logs.length === 0) {
90
+ print(`No logs found for Stack ${stackId}`)
91
+ return
92
+ }
93
+
94
+ print(`${display.blueprintsFormatting.formatTitle('Blueprint', name)} Logs`)
95
+ print(
96
+ `Found ${display.colors.bold(logs.length.toString())} log entries for stack ${display.colors.yellow(stackId)}\n`,
97
+ )
98
+
99
+ // Organize and format logs by day
100
+ const logsByDay = display.logsFormatting.organizeLogsByDay(logs)
101
+ print(display.logsFormatting.formatLogsByDay(logsByDay))
102
+ } catch (err) {
103
+ print('Failed to retrieve logs')
104
+ if (err instanceof Error) {
105
+ print(`Error: ${err.message}`)
106
+ }
107
+ }
108
+ },
109
+ }
110
+
111
+ export default logsBlueprintsCommand