@sanity/runtime-cli 7.6.7 → 8.0.1

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 (35) hide show
  1. package/README.md +65 -25
  2. package/bin/dev.cmd +1 -1
  3. package/bin/dev.js +1 -1
  4. package/dist/actions/blueprints/blueprint.d.ts +22 -4
  5. package/dist/actions/blueprints/blueprint.js +94 -39
  6. package/dist/actions/blueprints/resources.js +1 -13
  7. package/dist/actions/blueprints/stacks.d.ts +6 -0
  8. package/dist/actions/blueprints/stacks.js +17 -3
  9. package/dist/commands/blueprints/add.d.ts +1 -1
  10. package/dist/commands/blueprints/add.js +2 -5
  11. package/dist/commands/blueprints/config.js +1 -3
  12. package/dist/commands/blueprints/destroy.js +1 -4
  13. package/dist/commands/blueprints/info.js +1 -3
  14. package/dist/commands/blueprints/init.js +2 -6
  15. package/dist/commands/blueprints/stacks.d.ts +1 -2
  16. package/dist/commands/blueprints/stacks.js +3 -5
  17. package/dist/cores/blueprints/add.js +23 -7
  18. package/dist/cores/blueprints/config.js +3 -28
  19. package/dist/cores/blueprints/init.js +41 -74
  20. package/dist/cores/blueprints/plan.js +3 -3
  21. package/dist/cores/blueprints/stacks.d.ts +1 -1
  22. package/dist/cores/blueprints/stacks.js +2 -2
  23. package/dist/utils/display/blueprints-formatting.js +3 -3
  24. package/dist/utils/display/presenters.d.ts +1 -0
  25. package/dist/utils/display/presenters.js +6 -3
  26. package/dist/utils/display/prompt.d.ts +4 -0
  27. package/dist/utils/display/prompt.js +44 -1
  28. package/dist/utils/other/npmjs.d.ts +1 -0
  29. package/dist/utils/other/npmjs.js +13 -0
  30. package/oclif.manifest.json +18 -19
  31. package/package.json +6 -7
  32. package/dist/utils/format-error.d.ts +0 -4
  33. package/dist/utils/format-error.js +0 -9
  34. package/dist/utils/is-dependency.d.ts +0 -1
  35. package/dist/utils/is-dependency.js +0 -7
@@ -8,8 +8,7 @@ export default class ConfigCommand extends BlueprintCommand {
8
8
  '<%= config.bin %> <%= command.id %> --test-config',
9
9
  '<%= config.bin %> <%= command.id %> --edit',
10
10
  '<%= config.bin %> <%= command.id %> --edit --project-id <projectId>',
11
- // LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
12
- // '<%= config.bin %> <%= command.id %> --edit --project-id <projectId> --stack-id <stackId>',
11
+ '<%= config.bin %> <%= command.id %> --edit --project-id <projectId> --stack-id <stackId>',
13
12
  ];
14
13
  static flags = {
15
14
  'test-config': Flags.boolean({
@@ -32,7 +31,6 @@ export default class ConfigCommand extends BlueprintCommand {
32
31
  description: 'Update the Stack ID in the configuration. Requires --edit flag',
33
32
  aliases: ['stack', 'stackId'],
34
33
  dependsOn: ['edit'],
35
- hidden: true, // LAUNCH LIMIT: 1 Stack per Project
36
34
  }),
37
35
  };
38
36
  async run() {
@@ -5,8 +5,7 @@ export default class DestroyCommand extends BlueprintCommand {
5
5
  static description = 'Destroy a Blueprint deployment (will not delete local files)';
6
6
  static examples = [
7
7
  '<%= config.bin %> <%= command.id %>',
8
- // LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
9
- // '<%= config.bin %> <%= command.id %> --id ST-a1b2c3 --projectId a1b2c3 --force',
8
+ '<%= config.bin %> <%= command.id %> --stack-id <stackId> --project-id <projectId> --force --no-wait',
10
9
  ];
11
10
  static flags = {
12
11
  force: Flags.boolean({
@@ -18,12 +17,10 @@ export default class DestroyCommand extends BlueprintCommand {
18
17
  description: 'Project associated with the Stack (defaults to current Project)',
19
18
  aliases: ['projectId', 'project'],
20
19
  dependsOn: ['stack-id', 'force'],
21
- hidden: true, // LAUNCH LIMIT: 1 Stack per Project
22
20
  }),
23
21
  'stack-id': Flags.string({
24
22
  description: 'Stack ID to destroy (defaults to current Stack)',
25
23
  aliases: ['stackId', 'stack'],
26
- hidden: true, // LAUNCH LIMIT: 1 Stack per Project
27
24
  }),
28
25
  'no-wait': Flags.boolean({
29
26
  description: 'Do not wait for destruction to complete',
@@ -5,13 +5,11 @@ export default class InfoCommand extends DeployedBlueprintCommand {
5
5
  static description = 'Show information about a Blueprint deployment';
6
6
  static examples = [
7
7
  '<%= config.bin %> <%= command.id %>',
8
- // LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
9
- // '<%= config.bin %> <%= command.id %> --id ST-a1b2c3',
8
+ '<%= config.bin %> <%= command.id %> --stack-id <stackId>',
10
9
  ];
11
10
  static flags = {
12
11
  id: Flags.string({
13
12
  description: 'Stack ID to show info for (defaults to current stack)',
14
- hidden: true, // LAUNCH LIMIT: 1 Stack per Project
15
13
  }),
16
14
  };
17
15
  async run() {
@@ -7,10 +7,8 @@ export default class InitCommand extends Command {
7
7
  '<%= config.bin %> <%= command.id %>',
8
8
  '<%= config.bin %> <%= command.id %> [directory]',
9
9
  '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts>',
10
- '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId>',
11
- // LAUNCH LIMIT: 1 Stack per Project - do not prompt for Stack, just create one
12
- // '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-id <stackId>',
13
- // '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-name <stackName>',
10
+ '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-id <stackId>',
11
+ '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --stack-name <stackName>',
14
12
  ];
15
13
  static args = {
16
14
  dir: Args.string({
@@ -35,13 +33,11 @@ export default class InitCommand extends Command {
35
33
  aliases: ['stack', 'stackId'],
36
34
  dependsOn: ['project-id'],
37
35
  exclusive: ['stack-name'],
38
- hidden: true, // LAUNCH LIMIT: 1 Stack per Project
39
36
  }),
40
37
  'stack-name': Flags.string({
41
38
  description: 'Name to use for a NEW Stack',
42
39
  aliases: ['name'],
43
40
  exclusive: ['stack-id'],
44
- hidden: true, // LAUNCH LIMIT: 1 Stack per Project
45
41
  }),
46
42
  };
47
43
  async run() {
@@ -1,10 +1,9 @@
1
1
  import { BlueprintCommand } from '../../baseCommands.js';
2
2
  export default class StacksCommand extends BlueprintCommand<typeof StacksCommand> {
3
- static hidden: boolean;
4
3
  static description: string;
5
4
  static examples: string[];
6
5
  static flags: {
7
- projectId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
7
  };
9
8
  run(): Promise<void>;
10
9
  }
@@ -2,17 +2,15 @@ import { Flags } from '@oclif/core';
2
2
  import { BlueprintCommand } from '../../baseCommands.js';
3
3
  import { blueprintStacksCore } from '../../cores/blueprints/stacks.js';
4
4
  export default class StacksCommand extends BlueprintCommand {
5
- // LAUNCH LIMIT: 1 Stack per Project - hide stacks command
6
- static hidden = true;
7
5
  static description = 'List all Blueprint stacks';
8
6
  static examples = [
9
7
  '<%= config.bin %> <%= command.id %>',
10
- '<%= config.bin %> <%= command.id %> --projectId a1b2c3',
8
+ '<%= config.bin %> <%= command.id %> --project-id <projectId>',
11
9
  ];
12
10
  static flags = {
13
- projectId: Flags.string({
11
+ 'project-id': Flags.string({
14
12
  description: 'Project ID to show stacks for',
15
- required: false,
13
+ aliases: ['projectId', 'project'],
16
14
  }),
17
15
  };
18
16
  async run() {
@@ -1,9 +1,18 @@
1
1
  import { cwd } from 'node:process';
2
+ import { highlight } from 'cardinal';
2
3
  import chalk from 'chalk';
3
- import highlight from 'color-json';
4
4
  import inquirer from 'inquirer';
5
5
  import { createFunctionResource } from '../../actions/blueprints/resources.js';
6
6
  import { validateFunctionName } from '../../utils/validate/resource.js';
7
+ const FUNCTION_BLUEPRINT_RESOURCE_TEMPLATE = `
8
+ export default defineBlueprint({
9
+ // ...
10
+ resources: [
11
+ // ...
12
+ defineDocumentFunction({name: '{{fnName}}'}), // ← add this line
13
+ ],
14
+ })
15
+ `;
7
16
  export async function blueprintAddCore(options) {
8
17
  const { bin = 'sanity', log, blueprint, args, flags } = options;
9
18
  const { type: resourceType } = args;
@@ -89,17 +98,24 @@ export async function blueprintAddCore(options) {
89
98
  });
90
99
  log(`\nCreated function: ${filePath.replace(cwd(), '')}`);
91
100
  if (!resourceAdded) {
92
- // print the resource JSON for manual addition
93
- log('\nAdd this Function resource to your Blueprint:');
94
- log(highlight(JSON.stringify(resource, null, 2), undefined, undefined, 2));
101
+ // print the resource definition for manual addition
102
+ log(`\n${chalk.bold('Add the Resource to your Blueprint:')}`);
103
+ switch (blueprint.fileInfo.extension) {
104
+ case '.ts':
105
+ log(highlight(FUNCTION_BLUEPRINT_RESOURCE_TEMPLATE.replace('{{fnName}}', fnName)));
106
+ break;
107
+ case '.js':
108
+ log(highlight(FUNCTION_BLUEPRINT_RESOURCE_TEMPLATE.replace('{{fnName}}', fnName)));
109
+ break;
110
+ default:
111
+ log(highlight(JSON.stringify(resource, null, 2)));
112
+ break;
113
+ }
95
114
  }
96
115
  else {
97
116
  // added to blueprint.json
98
117
  log(`Function "${chalk.bold(fnName)}" added to Blueprint file.`);
99
118
  }
100
- if (fnLang === 'ts') {
101
- log(chalk.dim('Add "functions/**/.build/**" to your .gitignore.'));
102
- }
103
119
  return { success: true };
104
120
  }
105
121
  catch (err) {
@@ -1,9 +1,9 @@
1
+ import { highlight } from 'cardinal';
1
2
  import chalk from 'chalk';
2
- import highlight from 'color-json';
3
3
  import inquirer from 'inquirer';
4
4
  import ora from 'ora';
5
5
  import { BLUEPRINT_CONFIG_FILE, BLUEPRINT_DIR, writeConfigFile, } from '../../actions/blueprints/blueprint.js';
6
- import { createStack, getStack, listStacks } from '../../actions/blueprints/stacks.js';
6
+ import { createStack, getStack } from '../../actions/blueprints/stacks.js';
7
7
  import { getProject } from '../../actions/sanity/projects.js';
8
8
  import { niceId, warn } from '../../utils/display/presenters.js';
9
9
  import { promptForProject } from '../../utils/display/prompt.js';
@@ -67,7 +67,7 @@ export async function blueprintConfigCore(options) {
67
67
  // LAUNCH LIMIT: 1 Stack per Project - configStackId is always inferred from projectId if not set in config JSON
68
68
  let updatedStackId = flagStackId ?? // flag first
69
69
  configStackId ?? // existing config second
70
- `ST-${updatedProjectId}`; /*??*/ // LAUNCH LIMIT: 1 Stack per Project - project-based third
70
+ `ST-${updatedProjectId}`; //?? LAUNCH LIMIT: 1 Stack per Project - project-based third
71
71
  // (await promptForStackId({projectId: updatedProjectId, knownStackId: configStackId})) // prompt for stackId
72
72
  const isProjectBasedId = updatedStackId === `ST-${updatedProjectId}`;
73
73
  log(`\n${chalk.bold('New configuration:')}`);
@@ -135,31 +135,6 @@ export async function blueprintConfigCore(options) {
135
135
  return { success: false, error: 'Unknown error' };
136
136
  }
137
137
  }
138
- async function promptForStackId({ token, projectId, knownStackId, }) {
139
- const auth = { token, projectId };
140
- // get stacks for selected project
141
- const { ok: stacksOk, stacks, error: stacksError } = await listStacks(auth);
142
- if (!stacksOk)
143
- throw new Error(stacksError ?? 'Unknown error listing stacks');
144
- if (stacks.length > 0) {
145
- const stackChoices = stacks.map((s) => ({
146
- name: `"${s.name}" ${niceId(s.id)} ${chalk.dim(`(${s.resources.length} res)`)}`,
147
- value: s.id,
148
- }));
149
- stackChoices.push({ name: 'Unset Stack association', value: 'unset' });
150
- const { pickedStackId } = await inquirer.prompt([
151
- {
152
- type: 'list',
153
- name: 'pickedStackId',
154
- message: 'Select a stack:',
155
- choices: stackChoices,
156
- default: knownStackId,
157
- },
158
- ]);
159
- return pickedStackId === 'unset' ? undefined : pickedStackId;
160
- }
161
- return undefined;
162
- }
163
138
  async function testConfigAndReport({ token, stackId, projectId, }) {
164
139
  const spinner = ora('Testing the configuration...').start();
165
140
  const { ok, error } = await getStack({
@@ -2,18 +2,18 @@ import { join } from 'node:path';
2
2
  import { cwd } from 'node:process';
3
3
  import chalk from 'chalk';
4
4
  import inquirer from 'inquirer';
5
- import { BLUEPRINT_CONFIG_FILE, BLUEPRINT_DIR, findBlueprintFile, writeBlueprintToDisk, writeConfigFile, } from '../../actions/blueprints/blueprint.js';
6
- import { createStack, getStack, listStacks } from '../../actions/blueprints/stacks.js';
5
+ import { BLUEPRINT_CONFIG_FILE, BLUEPRINT_DIR, findBlueprintFile, writeBlueprintToDisk, writeConfigFile, writeGitignoreFile, writeOrUpdateNodeDependency, } from '../../actions/blueprints/blueprint.js';
6
+ import { createEmptyStack, getStack } from '../../actions/blueprints/stacks.js';
7
7
  import { getProject } from '../../actions/sanity/projects.js';
8
- import { niceId } from '../../utils/display/presenters.js';
9
- import { promptForProject } from '../../utils/display/prompt.js';
8
+ import { check, warn } from '../../utils/display/presenters.js';
9
+ import { promptForProject, promptForStackId } from '../../utils/display/prompt.js';
10
10
  const LAUNCH_LIMIT_STACK_PER_PROJECT = true;
11
11
  export async function blueprintInitCore(options) {
12
- const { log, token, args, flags } = options;
12
+ const { bin = 'sanity', log, token, args, flags } = options;
13
13
  try {
14
14
  const { 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId, 'stack-id': flagStackId, 'stack-name': flagStackName, dir: flagDir, } = flags;
15
15
  const { dir: argDir } = args;
16
- const dirProvided = argDir || flagDir;
16
+ const providedDir = argDir || flagDir;
17
17
  const here = cwd();
18
18
  const dir = argDir || flagDir || here;
19
19
  const existingBlueprint = findBlueprintFile(dir);
@@ -30,6 +30,7 @@ export async function blueprintInitCore(options) {
30
30
  const pickedProject = await promptForProject({ token });
31
31
  projectId = pickedProject.projectId;
32
32
  }
33
+ log('');
33
34
  if (flagStackName) {
34
35
  // using --stack-name gets around "LAUNCH LIMIT: 1 Stack per Project"
35
36
  const stack = await createEmptyStack({
@@ -51,17 +52,37 @@ export async function blueprintInitCore(options) {
51
52
  stackId = await promptForStackId({ projectId, token });
52
53
  }
53
54
  }
54
- const fileName = `blueprint.${blueprintExtension}`;
55
+ const fileName = `sanity.blueprint.${blueprintExtension}`;
55
56
  const filePath = join(dir, fileName);
56
- if (dirProvided)
57
- log(`New Blueprint created: ${dirProvided}/`);
58
57
  writeBlueprintToDisk({ blueprintFilePath: filePath });
59
- log(`Created new blueprint: ${dirProvided ?? '.'}/${fileName}`);
58
+ if (providedDir)
59
+ log(check(`${chalk.bold('New folder created:')} ${providedDir}/`));
60
+ log(check(`${chalk.bold('Created Blueprint:')} ${providedDir ?? '.'}/${fileName}`));
60
61
  writeConfigFile({ blueprintFilePath: filePath, projectId, stackId });
61
- log(`Created new config file: ${dirProvided ?? '.'}/${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`);
62
- if (blueprintExtension === 'ts') {
63
- log('\nNote: TypeScript support requires "tsx" to be installed. Run: npm install -D tsx');
62
+ log(check(`${chalk.bold('Added configuration:')} ${providedDir ?? '.'}/${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`));
63
+ writeGitignoreFile({ blueprintFilePath: filePath });
64
+ log(check(`${chalk.bold('Added .gitignore:')} ${providedDir ?? '.'}/.gitignore`));
65
+ if (blueprintExtension !== 'json') {
66
+ try {
67
+ // check for || create package.json and add @sanity/blueprints to dependencies
68
+ await writeOrUpdateNodeDependency({
69
+ blueprintFilePath: filePath,
70
+ dependency: '@sanity/blueprints',
71
+ });
72
+ log(check(`${chalk.bold('Added dependency:')} @sanity/blueprints`));
73
+ }
74
+ catch (err) {
75
+ log(warn('Unable to add @sanity/blueprints to your project.'));
76
+ }
64
77
  }
78
+ const nextStepParts = [];
79
+ if (providedDir)
80
+ nextStepParts.push(`cd ./${providedDir}`);
81
+ if (blueprintExtension !== 'json')
82
+ nextStepParts.push('npm install');
83
+ nextStepParts.push(`${bin} blueprints --help`);
84
+ const nextStep = `Run ${chalk.bold.magenta(nextStepParts.join(' && '))}`;
85
+ log(`\n ${nextStep} to get started`);
65
86
  return { success: true };
66
87
  }
67
88
  catch (error) {
@@ -75,72 +96,17 @@ async function promptForBlueprintType() {
75
96
  {
76
97
  type: 'list',
77
98
  name: 'pickedBlueprintsType',
78
- message: 'Choose a blueprint type:',
99
+ message: 'Choose a Blueprint file type:',
79
100
  choices: [
101
+ { name: 'TypeScript', value: 'ts' },
102
+ { name: 'JavaScript', value: 'js' },
80
103
  { name: 'JSON', value: 'json' },
81
- { name: 'TypeScript', value: 'ts', disabled: '(Coming soon)' },
82
- { name: 'JavaScript', value: 'js', disabled: '(Coming soon)' },
83
104
  ],
84
- default: 'json',
105
+ default: 'ts',
85
106
  },
86
107
  ]);
87
108
  return pickedBlueprintsType;
88
109
  }
89
- async function promptForStackId({ projectId, token, }) {
90
- const { ok: stacksOk, error: stacksErr, stacks } = await listStacks({ token, projectId });
91
- if (!stacksOk) {
92
- throw new Error(stacksErr || 'Failed to list Stacks');
93
- }
94
- const stackChoices = [
95
- { name: chalk.bold('✨ Create a new Stack'), value: 'new' },
96
- ];
97
- if (stacks.length > 0) {
98
- stackChoices.push(new inquirer.Separator(chalk.underline('Use an existing Stack:')));
99
- stackChoices.push(...stacks.map((s) => ({
100
- name: `"${s.name}" ${niceId(s.id)} ${chalk.dim(`(${s.resources.length} res)`)}`,
101
- value: s.id,
102
- })));
103
- }
104
- const { pickedStackId } = await inquirer.prompt([
105
- {
106
- type: 'list',
107
- name: 'pickedStackId',
108
- message: 'Select an existing deployment or create a new one:',
109
- choices: stackChoices,
110
- },
111
- ]);
112
- if (pickedStackId === 'new') {
113
- const { stackName } = await inquirer.prompt([
114
- {
115
- type: 'input',
116
- name: 'stackName',
117
- message: 'Enter a name for your Stack:',
118
- validate: (input) => input.length > 0 || 'Stack name is required',
119
- },
120
- ]);
121
- const stack = await createEmptyStack({
122
- token,
123
- projectId,
124
- name: stackName,
125
- });
126
- return stack.id;
127
- }
128
- return pickedStackId;
129
- }
130
- async function createEmptyStack({ token, projectId, name, projectBased = true, }) {
131
- const stackPayload = {
132
- name,
133
- projectId,
134
- useProjectBasedId: projectBased,
135
- document: { resources: [] },
136
- };
137
- const auth = { token, projectId };
138
- const response = await createStack({ stackPayload, auth });
139
- if (!response.ok) {
140
- throw new Error(response.error || 'Failed to create new Stack');
141
- }
142
- return response.stack;
143
- }
144
110
  // LAUNCH LIMIT: 1 Stack per Project - create exclusive stack for project
145
111
  async function createProjectBasedStack(auth, token, log) {
146
112
  const { projectId } = auth;
@@ -155,8 +121,9 @@ async function createProjectBasedStack(auth, token, log) {
155
121
  const { stack: existingStack, ok: stackOk } = await getStack({ stackId: inferredStackId, auth });
156
122
  // if existing stack, return stack
157
123
  if (stackOk && existingStack) {
158
- log(chalk.red(`Found existing deployment for "${projectDisplayName}" Blueprint`));
159
- log(chalk.red('Deploying an empty Blueprint will override the existing deployment.'));
124
+ log(warn(`"${projectDisplayName}" has an existing deployment.`));
125
+ log(warn(`Deploying an empty Blueprint ${chalk.bold.red('will override the existing deployment!')}`));
126
+ log('');
160
127
  return existingStack;
161
128
  }
162
129
  // if not, create a stack
@@ -1,10 +1,10 @@
1
1
  import chalk from 'chalk';
2
2
  import { getStack } from '../../actions/blueprints/stacks.js';
3
- import { formatResourceTree, formatTitle, stackDeployDiff, } from '../../utils/display/blueprints-formatting.js';
3
+ import { formatResourceTree, stackDeployDiff } from '../../utils/display/blueprints-formatting.js';
4
4
  export async function blueprintPlanCore(options) {
5
5
  const { bin = 'sanity', log, blueprint, token } = options;
6
6
  const { projectId, stackId, parsedBlueprint, fileInfo } = blueprint;
7
- log(`${formatTitle('Deployment', 'Plan')} ${chalk.dim(`(${fileInfo.fileName})`)}`);
7
+ log(`${chalk.bold.blueBright('Blueprint Deployment Plan')} ${chalk.dim(`(${fileInfo.fileName})`)}`);
8
8
  log(formatResourceTree(parsedBlueprint.resources));
9
9
  if (token && projectId && stackId) {
10
10
  const stackResponse = await getStack({ stackId, auth: { token, projectId } });
@@ -19,6 +19,6 @@ export async function blueprintPlanCore(options) {
19
19
  log(chalk.dim('No changes detected to live deployment'));
20
20
  }
21
21
  }
22
- log(chalk.dim(`Run ${bin} blueprints deploy to deploy these changes`));
22
+ log(chalk.dim(`Run "${bin} blueprints deploy" to deploy these changes`));
23
23
  return { success: true };
24
24
  }
@@ -4,7 +4,7 @@ export interface BlueprintStacksOptions extends CoreConfig {
4
4
  token: string;
5
5
  blueprint: ReadBlueprintResult;
6
6
  flags: {
7
- projectId?: string;
7
+ 'project-id'?: string;
8
8
  };
9
9
  }
10
10
  export declare function blueprintStacksCore(options: BlueprintStacksOptions): Promise<CoreResult>;
@@ -5,9 +5,9 @@ import { niceId } from '../../utils/display/presenters.js';
5
5
  export async function blueprintStacksCore(options) {
6
6
  const { log, token, blueprint, flags } = options;
7
7
  const { projectId: blueprintProjectId, stackId: blueprintStackId } = blueprint;
8
- const projectId = flags.projectId || blueprintProjectId;
8
+ const projectId = flags['project-id'] || blueprintProjectId;
9
9
  if (!projectId) {
10
- log('Run in a Blueprint directory or provide a Project with --projectId');
10
+ log('Run in a Blueprint directory or provide a Project with --project-id');
11
11
  return { success: false, error: 'No Project ID provided' };
12
12
  }
13
13
  try {
@@ -9,7 +9,7 @@ export function formatTitle(title, name) {
9
9
  export function formatResourceTree(resources) {
10
10
  if (!resources || resources.length === 0)
11
11
  return ' Zero resources';
12
- const output = [`${chalk.blue('Blueprint Resources')} [${resources.length}]`];
12
+ const output = [`${chalk.bold.underline('Resources')} [${resources.length}]`];
13
13
  const functionResources = resources.filter((r) => r.type?.startsWith('sanity.function.'));
14
14
  const otherResources = resources.filter((r) => !r.type?.startsWith('sanity.function.'));
15
15
  const hasOtherResources = otherResources.length > 0;
@@ -17,12 +17,12 @@ export function formatResourceTree(resources) {
17
17
  const fnsOutput = [`${chalk.bold('Functions')} [${functionResources.length}]`];
18
18
  const fnDetails = [];
19
19
  for (const fn of functionResources) {
20
- const name = chalk.green(fn.displayName || fn.name);
20
+ const name = chalk.bold.green(fn.displayName || fn.name);
21
21
  const ids = [
22
22
  'id' in fn && fn.id ? `${niceId(fn.id)}` : '',
23
23
  'externalId' in fn && fn.externalId ? `<${niceId(fn.externalId)}>` : '',
24
24
  ].join(' ');
25
- fnDetails.push(`"${name}" ${ids}`);
25
+ fnDetails.push(`${name} ${ids}`);
26
26
  if ('parameters' in fn && fn.parameters) {
27
27
  fnDetails.push(arrayifyStackFunction(fn));
28
28
  }
@@ -1,3 +1,4 @@
1
+ export declare function check(str: string): string;
1
2
  export declare function info(str: string): string;
2
3
  export declare function warn(str: string): string;
3
4
  export declare function severe(str: string): string;
@@ -1,12 +1,15 @@
1
1
  import chalk from 'chalk';
2
+ export function check(str) {
3
+ return `${chalk.bold(chalk.green('✔︎'))} ${str}`;
4
+ }
2
5
  export function info(str) {
3
- return `${chalk.blue('')} ${str}`;
6
+ return `${chalk.bold.blue('ℹ︎')} ${str}`;
4
7
  }
5
8
  export function warn(str) {
6
- return `${chalk.yellow('')} ${str}`;
9
+ return `${chalk.bold.yellow('▶︎')} ${str}`;
7
10
  }
8
11
  export function severe(str) {
9
- return `${chalk.red('')} ${str}`;
12
+ return `${chalk.bold.red('')} ${str}`;
10
13
  }
11
14
  export function niceId(id) {
12
15
  if (!id)
@@ -12,3 +12,7 @@ export declare function promptForProject({ token, knownOrganizationId, knownProj
12
12
  projectId: string;
13
13
  displayName: string;
14
14
  }>;
15
+ export declare function promptForStackId({ projectId, token, }: {
16
+ projectId: string;
17
+ token: string;
18
+ }): Promise<string>;
@@ -1,4 +1,6 @@
1
+ import chalk from 'chalk';
1
2
  import inquirer from 'inquirer';
3
+ import { createEmptyStack, listStacks } from '../../actions/blueprints/stacks.js';
2
4
  import { groupProjectsByOrganization } from '../../actions/sanity/projects.js';
3
5
  import { niceId } from './presenters.js';
4
6
  /**
@@ -44,10 +46,51 @@ export async function promptForProject({ token, knownOrganizationId, knownProjec
44
46
  {
45
47
  type: 'list',
46
48
  name: 'pickedProject',
47
- message: 'Which Project would you like to use?',
49
+ message: 'Choose a Sanity Project:',
48
50
  choices: projectChoices,
49
51
  default: knownProjectId,
50
52
  },
51
53
  ]);
52
54
  return pickedProject;
53
55
  }
56
+ export async function promptForStackId({ projectId, token, }) {
57
+ const { ok: stacksOk, error: stacksErr, stacks } = await listStacks({ token, projectId });
58
+ if (!stacksOk) {
59
+ throw new Error(stacksErr || 'Failed to list Stacks');
60
+ }
61
+ const stackChoices = [
62
+ { name: chalk.bold('✨ Create a new Stack'), value: 'new' },
63
+ ];
64
+ if (stacks.length > 0) {
65
+ stackChoices.push(new inquirer.Separator(chalk.underline('Use an existing Stack:')));
66
+ stackChoices.push(...stacks.map((s) => ({
67
+ name: `"${s.name}" ${niceId(s.id)} ${chalk.dim(`(${s.resources.length} res)`)}`,
68
+ value: s.id,
69
+ })));
70
+ }
71
+ const { pickedStackId } = await inquirer.prompt([
72
+ {
73
+ type: 'list',
74
+ name: 'pickedStackId',
75
+ message: 'Select an existing deployment or create a new one:',
76
+ choices: stackChoices,
77
+ },
78
+ ]);
79
+ if (pickedStackId === 'new') {
80
+ const { stackName } = await inquirer.prompt([
81
+ {
82
+ type: 'input',
83
+ name: 'stackName',
84
+ message: 'Enter a name for your Stack:',
85
+ validate: (input) => input.length > 0 || 'Stack name is required',
86
+ },
87
+ ]);
88
+ const stack = await createEmptyStack({
89
+ token,
90
+ projectId,
91
+ name: stackName,
92
+ });
93
+ return stack.id;
94
+ }
95
+ return pickedStackId;
96
+ }
@@ -0,0 +1 @@
1
+ export declare function getLatestNpmVersion(pkg: string): Promise<string>;
@@ -0,0 +1,13 @@
1
+ export async function getLatestNpmVersion(pkg) {
2
+ const url = `https://registry.npmjs.org/${pkg}/latest`;
3
+ try {
4
+ const res = await fetch(url);
5
+ if (!res.ok)
6
+ throw new Error(`Failed to fetch version for ${pkg}`);
7
+ const data = await res.json();
8
+ return data.version;
9
+ }
10
+ catch (error) {
11
+ return 'latest';
12
+ }
13
+ }