@sanity/runtime-cli 10.11.3 → 11.0.3

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 (54) hide show
  1. package/README.md +84 -34
  2. package/dist/actions/blueprints/index.d.ts +2 -29
  3. package/dist/actions/blueprints/index.js +0 -42
  4. package/dist/baseCommands.d.ts +5 -4
  5. package/dist/baseCommands.js +6 -4
  6. package/dist/commands/blueprints/add.d.ts +5 -0
  7. package/dist/commands/blueprints/add.js +34 -8
  8. package/dist/commands/blueprints/config.d.ts +1 -0
  9. package/dist/commands/blueprints/config.js +5 -0
  10. package/dist/commands/blueprints/deploy.js +2 -1
  11. package/dist/commands/blueprints/destroy.d.ts +1 -0
  12. package/dist/commands/blueprints/destroy.js +6 -1
  13. package/dist/commands/blueprints/init.d.ts +1 -0
  14. package/dist/commands/blueprints/init.js +4 -0
  15. package/dist/commands/blueprints/stacks.d.ts +1 -0
  16. package/dist/commands/blueprints/stacks.js +7 -0
  17. package/dist/commands/functions/add.d.ts +16 -0
  18. package/dist/commands/functions/add.js +65 -0
  19. package/dist/commands/functions/env/add.js +2 -1
  20. package/dist/commands/functions/env/list.js +2 -1
  21. package/dist/commands/functions/env/remove.js +2 -1
  22. package/dist/commands/functions/logs.js +2 -1
  23. package/dist/commands/functions/test.d.ts +1 -0
  24. package/dist/commands/functions/test.js +5 -0
  25. package/dist/cores/blueprints/config.d.ts +1 -0
  26. package/dist/cores/blueprints/config.js +45 -149
  27. package/dist/cores/blueprints/deploy.d.ts +3 -2
  28. package/dist/cores/blueprints/deploy.js +3 -3
  29. package/dist/cores/blueprints/destroy.d.ts +1 -0
  30. package/dist/cores/blueprints/destroy.js +20 -7
  31. package/dist/cores/blueprints/doctor.js +43 -4
  32. package/dist/cores/blueprints/index.d.ts +0 -2
  33. package/dist/cores/blueprints/index.js +0 -1
  34. package/dist/cores/blueprints/init.d.ts +1 -0
  35. package/dist/cores/blueprints/init.js +3 -1
  36. package/dist/cores/blueprints/plan.js +3 -3
  37. package/dist/cores/blueprints/stacks.d.ts +1 -0
  38. package/dist/cores/blueprints/stacks.js +22 -7
  39. package/dist/cores/{blueprints → functions}/add.d.ts +5 -8
  40. package/dist/cores/{blueprints → functions}/add.js +22 -43
  41. package/dist/cores/functions/index.d.ts +2 -0
  42. package/dist/cores/functions/index.js +1 -0
  43. package/dist/cores/functions/test.d.ts +1 -0
  44. package/dist/cores/functions/test.js +2 -4
  45. package/dist/cores/index.d.ts +3 -2
  46. package/dist/cores/index.js +9 -8
  47. package/dist/server/static/index.html +1 -1
  48. package/dist/server/static/vendor/vendor.bundle.js +30 -5
  49. package/dist/utils/display/errors.js +5 -2
  50. package/dist/utils/display/presenters.d.ts +1 -0
  51. package/dist/utils/display/presenters.js +3 -0
  52. package/dist/utils/types.d.ts +1 -0
  53. package/oclif.manifest.json +212 -3
  54. package/package.json +23 -26
@@ -0,0 +1,16 @@
1
+ import { BlueprintCommand } from '../../baseCommands.js';
2
+ export default class AddCommand extends BlueprintCommand<typeof AddCommand> {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ example: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ type: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ language: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ javascript: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ helpers: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ installer: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ install: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,65 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { BlueprintCommand } from '../../baseCommands.js';
3
+ import { functionAddCore } from '../../cores/functions/index.js';
4
+ export default class AddCommand extends BlueprintCommand {
5
+ static description = 'Add a Function to your Blueprint';
6
+ static examples = [
7
+ '<%= config.bin %> <%= command.id %>',
8
+ '<%= config.bin %> <%= command.id %> --helpers',
9
+ '<%= config.bin %> <%= command.id %> --name my-function',
10
+ '<%= config.bin %> <%= command.id %> --name my-function --type document-create',
11
+ '<%= config.bin %> <%= command.id %> --name my-function --type document-create --type document-update --lang js',
12
+ ];
13
+ static flags = {
14
+ example: Flags.string({
15
+ description: 'Example to use for the Function',
16
+ aliases: ['recipe'],
17
+ exclusive: ['name', 'fn-type', 'language', 'javascript', 'fn-helpers', 'fn-installer'],
18
+ }),
19
+ name: Flags.string({
20
+ description: 'Name of the Function to add',
21
+ char: 'n',
22
+ }),
23
+ type: Flags.string({
24
+ description: 'Document change event(s) that should trigger the function; you can specify multiple events by specifying this flag multiple times',
25
+ options: ['document-create', 'document-delete', 'document-update', 'document-publish'],
26
+ multiple: true,
27
+ multipleNonGreedy: true,
28
+ dependsOn: ['name'],
29
+ }),
30
+ language: Flags.string({
31
+ description: 'Language of the new Function',
32
+ aliases: ['lang'],
33
+ options: ['ts', 'js'],
34
+ default: 'ts',
35
+ }),
36
+ javascript: Flags.boolean({
37
+ description: 'Use JavaScript instead of TypeScript',
38
+ aliases: ['js'],
39
+ exclusive: ['language'],
40
+ }),
41
+ helpers: Flags.boolean({
42
+ description: 'Add helpers to the new Function',
43
+ allowNo: true,
44
+ }),
45
+ installer: Flags.string({
46
+ description: 'How to install the @sanity/functions helpers',
47
+ options: ['skip', 'npm', 'pnpm', 'yarn'],
48
+ }),
49
+ install: Flags.boolean({
50
+ description: 'Shortcut for --fn-installer npm',
51
+ char: 'i',
52
+ exclusive: ['fn-installer'],
53
+ }),
54
+ };
55
+ async run() {
56
+ const { success, error } = await functionAddCore({
57
+ bin: this.config.bin,
58
+ log: (msg) => this.log(msg),
59
+ blueprint: this.blueprint,
60
+ flags: this.flags,
61
+ });
62
+ if (!success)
63
+ this.error(error);
64
+ }
65
+ }
@@ -19,7 +19,8 @@ export default class EnvAddCommand extends DeployedBlueprintCommand {
19
19
  auth: this.auth,
20
20
  blueprint: this.blueprint,
21
21
  deployedStack: this.deployedStack,
22
- projectId: this.projectId,
22
+ scopeType: this.scopeType,
23
+ scopeId: this.scopeId,
23
24
  token: this.sanityToken,
24
25
  stackId: this.stackId,
25
26
  });
@@ -15,7 +15,8 @@ export default class EnvListCommand extends DeployedBlueprintCommand {
15
15
  auth: this.auth,
16
16
  blueprint: this.blueprint,
17
17
  deployedStack: this.deployedStack,
18
- projectId: this.projectId,
18
+ scopeType: this.scopeType,
19
+ scopeId: this.scopeId,
19
20
  token: this.sanityToken,
20
21
  stackId: this.stackId,
21
22
  });
@@ -16,7 +16,8 @@ export default class EnvRemoveCommand extends DeployedBlueprintCommand {
16
16
  auth: this.auth,
17
17
  blueprint: this.blueprint,
18
18
  deployedStack: this.deployedStack,
19
- projectId: this.projectId,
19
+ scopeType: this.scopeType,
20
+ scopeId: this.scopeId,
20
21
  token: this.sanityToken,
21
22
  stackId: this.stackId,
22
23
  });
@@ -56,7 +56,8 @@ export default class LogsCommand extends DeployedBlueprintCommand {
56
56
  auth: this.auth,
57
57
  blueprint: this.blueprint,
58
58
  deployedStack: this.deployedStack,
59
- projectId: this.projectId,
59
+ scopeType: this.scopeType,
60
+ scopeId: this.scopeId,
60
61
  token: this.sanityToken,
61
62
  stackId: this.stackId,
62
63
  });
@@ -17,6 +17,7 @@ export default class TestCommand extends BlueprintCommand<typeof TestCommand> {
17
17
  api: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
18
  dataset: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
19
  'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ 'organization-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
21
  'document-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
22
  'document-id-before': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
23
  'document-id-after': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -102,6 +102,11 @@ export default class TestCommand extends BlueprintCommand {
102
102
  aliases: ['project', 'projectId'],
103
103
  required: false,
104
104
  }),
105
+ 'organization-id': Flags.string({
106
+ description: 'Sanity Organization ID to use',
107
+ aliases: ['organization', 'organizationId', 'org'],
108
+ required: false,
109
+ }),
105
110
  'document-id': Flags.string({
106
111
  description: 'Document to fetch and send to function',
107
112
  aliases: ['doc', 'documentId'],
@@ -7,6 +7,7 @@ export interface BlueprintConfigOptions extends CoreConfig {
7
7
  'test-config'?: boolean;
8
8
  edit?: boolean;
9
9
  'project-id'?: string;
10
+ 'organization-id'?: string;
10
11
  'stack-id'?: string;
11
12
  };
12
13
  }
@@ -1,32 +1,28 @@
1
1
  import { highlight } from 'cardinal';
2
2
  import chalk from 'chalk';
3
- import inquirer from 'inquirer';
4
- import ora from 'ora';
5
3
  import { writeConfigFile } from '../../actions/blueprints/config.js';
6
- import { createStack, getStack } from '../../actions/blueprints/stacks.js';
7
- import { getProject } from '../../actions/sanity/projects.js';
8
4
  import { BLUEPRINT_CONFIG_FILE, BLUEPRINT_DIR } from '../../config.js';
9
- import { niceId, warn } from '../../utils/display/presenters.js';
5
+ import { capitalize, niceId, warn } from '../../utils/display/presenters.js';
10
6
  import { promptForProject } from '../../utils/display/prompt.js';
11
7
  export async function blueprintConfigCore(options) {
12
8
  const { bin = 'sanity', log, token, flags } = options;
13
- const { edit: editConfig = false, 'test-config': testConfig = false, 'project-id': flagProjectId, 'stack-id': flagStackId, } = flags;
9
+ const { edit: editConfig = false, 'test-config': testConfig = false, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, 'stack-id': flagStackId, } = flags;
14
10
  const { blueprint } = options;
15
- const { stackId: configStackId, projectId: configProjectId } = blueprint;
11
+ const { stackId: configStackId, scopeType: configScopeType, scopeId: configScopeId } = blueprint;
16
12
  try {
17
- if (!configStackId && !configProjectId) {
18
- log(warn('Missing local configuration.'));
13
+ if (!configStackId && !configScopeType && !configScopeId) {
14
+ log(warn('Incomplete configuration.'));
19
15
  if (!editConfig) {
20
16
  // blueprint.json exists but no config JSON
21
- log(`Use \`${bin} blueprints config --edit\` to set a configuration.`);
17
+ log(`Run \`${bin} blueprints doctor\` for diagnostics.`);
22
18
  return { success: true }; // not necessarily fatal
23
19
  }
24
20
  }
25
21
  log(chalk.bold('Current configuration:'));
26
- log(` Sanity Project: ${niceId(configProjectId)}`);
27
- log(` Deployment ID: ${niceId(configStackId)}`);
22
+ log(` Blueprint scoped to: ${chalk.blue(capitalize(configScopeType || 'unknown'))} ${niceId(configScopeId || 'unknown')}`);
23
+ log(` Deployment ID: ${niceId(configStackId || 'unknown')}`);
28
24
  // passing new config without --edit flag is not allowed
29
- if ((flagProjectId || flagStackId) && !editConfig) {
25
+ if ((flagProjectId || flagOrganizationId || flagStackId) && !editConfig) {
30
26
  log('To update the configuration, use the --edit flag.');
31
27
  return { success: true };
32
28
  }
@@ -37,100 +33,51 @@ export async function blueprintConfigCore(options) {
37
33
  if (testConfig) {
38
34
  log(warn(`The "test" flag is deprecated. Use "${bin} blueprints doctor" instead.`));
39
35
  }
40
- // testing without editing
41
- if (testConfig && !editConfig) {
42
- if (configStackId && configProjectId) {
43
- const testResult = await testConfigAndReport({
44
- token,
45
- stackId: configStackId,
46
- projectId: configProjectId,
47
- });
48
- if (!testResult.ok) {
49
- // command should exit with error code 1
50
- return { success: false, error: 'Existing Blueprint deployment not found.' };
51
- }
52
- return { success: testResult.ok };
53
- }
54
- return {
55
- success: false,
56
- error: 'Unable to test the configuration. Both Project and Stack IDs must be set.',
57
- };
58
- }
59
36
  // editing...
60
37
  if (editConfig) {
61
- const updatedProjectId = flagProjectId ||
62
- (await promptForProject({
63
- token,
64
- knownProjectId: configProjectId,
65
- })).projectId;
66
- if (!updatedProjectId) {
67
- return {
68
- success: false,
69
- error: 'Project ID is required.',
70
- };
71
- }
72
- // LAUNCH LIMIT: 1 Stack per Project - configStackId is always inferred from projectId if not set in config JSON
73
- let updatedStackId = flagStackId ?? // flag first
74
- configStackId ?? // existing config second
75
- `ST-${updatedProjectId}`; //?? LAUNCH LIMIT: 1 Stack per Project - project-based third
76
- // (await promptForStackId({projectId: updatedProjectId, knownStackId: configStackId})) // prompt for stackId
77
- const isProjectBasedId = updatedStackId === `ST-${updatedProjectId}`;
78
- log(`\n${chalk.bold('New configuration:')}`);
79
- log(` Sanity Project: ${niceId(updatedProjectId)}`);
80
- log(` Deployment ID: ${niceId(updatedStackId)}`);
81
- if (testConfig) {
82
- if (updatedProjectId && updatedStackId) {
83
- const { ok: newConfigOk, error: configError } = await testConfigAndReport({
38
+ if (configScopeType === 'project') {
39
+ // maintain original project-based flow
40
+ const updatedProjectId = flagProjectId ||
41
+ (await promptForProject({
84
42
  token,
85
- stackId: updatedStackId,
86
- projectId: updatedProjectId,
87
- });
88
- if (!newConfigOk) {
89
- // is this a projectBasedId stack?
90
- if (isProjectBasedId) {
91
- log(warn('Existing Blueprint deployment not found.'));
92
- const reinitResult = await startReinitializeStack({
93
- token,
94
- projectId: updatedProjectId,
95
- stackId: updatedStackId,
96
- });
97
- if (!reinitResult.ok) {
98
- return { success: false, error: reinitResult.error || 'Failed to reinitialize stack' };
99
- }
100
- log(`New Blueprint deployment created for "${reinitResult.projectDisplayName}"`);
101
- // continuing to save the config
102
- }
103
- else {
104
- return {
105
- success: false,
106
- error: configError || 'Updated configuration has not been saved.',
107
- };
108
- }
109
- }
43
+ knownProjectId: configScopeId,
44
+ })).projectId;
45
+ if (!updatedProjectId) {
46
+ return {
47
+ success: false,
48
+ error: 'Project ID is required.',
49
+ };
110
50
  }
111
- else {
51
+ // LAUNCH LIMIT: 1 Stack per Project - configStackId is always inferred from projectId if not set in config JSON
52
+ let updatedStackId = flagStackId ?? // flag first
53
+ configStackId ?? // existing config second
54
+ `ST-${updatedProjectId}`; //?? LAUNCH LIMIT: 1 Stack per Project - project-based third
55
+ // (await promptForStackId({projectId: updatedProjectId, knownStackId: configStackId})) // prompt for stackId
56
+ const isProjectBasedId = updatedStackId === `ST-${updatedProjectId}`;
57
+ log(`\n${chalk.bold('New configuration:')}`);
58
+ log(` Blueprint scoped to: ${chalk.blue(capitalize(configScopeType))} ${niceId(updatedProjectId)}`);
59
+ log(` Deployment ID: ${niceId(updatedStackId)}`);
60
+ // LAUNCH LIMIT: 1 Stack per Project - do not set Stack ID if it's project-based
61
+ if (isProjectBasedId)
62
+ updatedStackId = undefined;
63
+ try {
64
+ // update or create config JSON
65
+ writeConfigFile({ projectId: updatedProjectId, stackId: updatedStackId });
66
+ log('Configuration updated successfully.');
67
+ return { success: true };
68
+ }
69
+ catch {
70
+ log(`Unable to update config. These values should be set in ${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`);
71
+ log(highlight(JSON.stringify({ metadata: { projectId: updatedProjectId, stackId: updatedStackId } }, null, 2)));
112
72
  return {
113
73
  success: false,
114
- error: 'Unable to test the configuration. Both Project and Stack IDs must be set.',
74
+ error: `Be sure to update your ${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`,
115
75
  };
116
76
  }
117
77
  }
118
- // LAUNCH LIMIT: 1 Stack per Project - do not set Stack ID if it's project-based
119
- if (isProjectBasedId)
120
- updatedStackId = undefined;
121
- try {
122
- // update or create config JSON
123
- writeConfigFile({ projectId: updatedProjectId, stackId: updatedStackId });
124
- log('Configuration updated successfully.');
125
- return { success: true };
126
- }
127
- catch {
128
- log(`Unable to update config. These values should be set in ${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`);
129
- log(highlight(JSON.stringify({ metadata: { projectId: updatedProjectId, stackId: updatedStackId } }, null, 2)));
130
- return {
131
- success: false,
132
- error: `Be sure to update your ${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`,
133
- };
78
+ else {
79
+ log(warn('Only project-based Blueprints are currently supported.'));
80
+ return { success: false, error: 'Only project-based Blueprints are supported.' };
134
81
  }
135
82
  }
136
83
  // Default return (shouldn't reach here with proper flow control)
@@ -140,54 +87,3 @@ export async function blueprintConfigCore(options) {
140
87
  return { success: false, error: 'Unknown error' };
141
88
  }
142
89
  }
143
- async function testConfigAndReport({ token, stackId, projectId, }) {
144
- const spinner = ora('Testing the configuration...').start();
145
- const { ok, error } = await getStack({
146
- stackId,
147
- auth: { token, scopeType: 'project', scopeId: projectId },
148
- });
149
- if (!ok) {
150
- spinner.fail('Configuration test failed.');
151
- }
152
- else {
153
- spinner.succeed('Configuration is valid.');
154
- }
155
- return { ok, error };
156
- }
157
- async function startReinitializeStack({ token, projectId, }) {
158
- const auth = { token, scopeType: 'project', scopeId: projectId };
159
- // stack id IS ST-${projectId} – it has already been checked and doesn't exist
160
- const { confirm } = await inquirer.prompt([
161
- {
162
- type: 'confirm',
163
- name: 'confirm',
164
- message: `Do you want to create a ${chalk.blue('new')}, empty Blueprint deployment with the ${chalk.blue('existing')} configuration?`,
165
- },
166
- ]);
167
- if (!confirm)
168
- return { ok: false, error: 'Reinitialization cancelled.' };
169
- const { ok: projectOk, project, error: projectError } = await getProject(auth);
170
- if (!projectOk) {
171
- return {
172
- ok: false,
173
- error: projectError || 'Failed to find Project while creating Stack',
174
- };
175
- }
176
- const projectDisplayName = project.displayName;
177
- const response = await createStack({
178
- stackMutation: {
179
- name: projectDisplayName,
180
- scopeType: 'project',
181
- scopeId: projectId,
182
- document: { resources: [] },
183
- },
184
- auth,
185
- });
186
- if (!response.ok)
187
- return { ok: false, error: response.error || 'Failed to create new Stack' };
188
- return {
189
- ok: true,
190
- stackId: response.stack.id,
191
- projectDisplayName,
192
- };
193
- }
@@ -1,10 +1,11 @@
1
1
  import type { ReadBlueprintResult } from '../../actions/blueprints/blueprint.js';
2
- import type { AuthParams, Stack } from '../../utils/types.js';
2
+ import type { AuthParams, ScopeType, Stack } from '../../utils/types.js';
3
3
  import type { CoreConfig, CoreResult } from '../index.js';
4
4
  export interface BlueprintDeployOptions extends CoreConfig {
5
5
  auth: AuthParams;
6
6
  stackId: string;
7
- projectId: string;
7
+ scopeType: ScopeType;
8
+ scopeId: string;
8
9
  deployedStack: Stack;
9
10
  blueprint: ReadBlueprintResult;
10
11
  flags: {
@@ -7,7 +7,7 @@ import { getStack, updateStack } from '../../actions/blueprints/stacks.js';
7
7
  import { niceId } from '../../utils/display/presenters.js';
8
8
  import { isLocalFunctionResource } from '../../utils/types.js';
9
9
  export async function blueprintDeployCore(options) {
10
- const { bin = 'sanity', log, auth, stackId, projectId, deployedStack, blueprint, flags } = options;
10
+ const { bin = 'sanity', log, auth, stackId, scopeType, scopeId, deployedStack, blueprint, flags, } = options;
11
11
  const { verbose } = flags;
12
12
  const noWait = flags['no-wait'] || false;
13
13
  log(`Deploying "${deployedStack.name}" ${niceId(deployedStack.id)}...`);
@@ -51,8 +51,8 @@ export async function blueprintDeployCore(options) {
51
51
  const { ok: deployOk, stack, error: deployError, } = await updateStack({
52
52
  stackId,
53
53
  stackMutation: {
54
- scopeType: 'project',
55
- scopeId: projectId,
54
+ scopeType,
55
+ scopeId,
56
56
  name: deployedStack.name,
57
57
  document: { resources: validResources },
58
58
  },
@@ -6,6 +6,7 @@ export interface BlueprintDestroyOptions extends CoreConfig {
6
6
  flags: {
7
7
  force?: boolean;
8
8
  'project-id'?: string;
9
+ 'organization-id'?: string;
9
10
  'stack-id'?: string;
10
11
  'no-wait'?: boolean;
11
12
  };
@@ -7,22 +7,35 @@ import { destroyStack, getStack } from '../../actions/blueprints/stacks.js';
7
7
  import { niceId } from '../../utils/display/presenters.js';
8
8
  export async function blueprintDestroyCore(options) {
9
9
  const { log, token, blueprint, flags } = options;
10
- const { force = false, 'project-id': flagProjectId, 'stack-id': flagStackId, 'no-wait': noWait = false, } = flags;
10
+ const { force = false, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, 'stack-id': flagStackId, 'no-wait': noWait = false, } = flags;
11
11
  // 3-flag combo: just destroy it
12
- if (flagProjectId && flagStackId && force) {
12
+ if ((flagProjectId || flagOrganizationId) && flagStackId && force) {
13
+ let scopeType;
14
+ let scopeId;
15
+ if (flagOrganizationId) {
16
+ scopeType = 'organization';
17
+ scopeId = flagOrganizationId;
18
+ }
19
+ else if (flagProjectId) {
20
+ scopeType = 'project';
21
+ scopeId = flagProjectId;
22
+ }
23
+ else {
24
+ return { success: false, error: 'Unable to determine scope for Blueprint Destroy' };
25
+ }
13
26
  const { ok, error, stack } = await destroyStack({
14
27
  stackId: flagStackId,
15
- auth: { token, scopeType: 'project', scopeId: flagProjectId },
28
+ auth: { token, scopeType, scopeId },
16
29
  });
17
30
  if (!ok)
18
31
  return { success: false, error: error || 'Failed to destroy deployment' };
19
32
  log(`Deployment "${stack.name}" ${niceId(stack.id)} destroyed`);
20
33
  return { success: true };
21
34
  }
22
- const { projectId, stackId } = blueprint;
23
- if (!projectId)
24
- return { success: false, error: 'Project ID is required' };
25
- const auth = { token, scopeType: 'project', scopeId: projectId };
35
+ const { scopeType, scopeId, stackId } = blueprint;
36
+ if (!scopeType || !scopeId)
37
+ return { success: false, error: 'Scope is required' };
38
+ const auth = { token, scopeType, scopeId };
26
39
  let stack;
27
40
  try {
28
41
  if (flagStackId) {
@@ -1,10 +1,12 @@
1
1
  import { cwd } from 'node:process';
2
2
  import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
3
4
  import ora from 'ora';
4
5
  import { findBlueprintFile, readLocalBlueprint, } from '../../actions/blueprints/blueprint.js';
5
- import { getStack } from '../../actions/blueprints/stacks.js';
6
+ import { createStack, getStack } from '../../actions/blueprints/stacks.js';
7
+ import { getProject } from '../../actions/sanity/projects.js';
6
8
  import config from '../../config.js';
7
- import { check, indent, niceId, severe, warn } from '../../utils/display/presenters.js';
9
+ import { capitalize, check, indent, niceId, severe, warn } from '../../utils/display/presenters.js';
8
10
  import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
9
11
  const diagLookup = {
10
12
  online: 'Online',
@@ -107,9 +109,8 @@ export async function blueprintDoctorCore(options) {
107
109
  if (scopeType && scopeId && stackId) {
108
110
  diagnostics.configFileValid = true;
109
111
  if (v) {
110
- const capitalizedScopeType = scopeType.charAt(0).toUpperCase() + scopeType.slice(1);
111
112
  const configOutput = [
112
- `${capitalizedScopeType}: ${niceId(scopeId)}`,
113
+ `${capitalize(scopeType)}: ${niceId(scopeId)}`,
113
114
  `Deployment: ${niceId(stackId)}`,
114
115
  ].join('\n');
115
116
  log(indent(configOutput));
@@ -186,3 +187,41 @@ export async function blueprintDoctorCore(options) {
186
187
  return { success: false, error: 'One or more checks failed', data: { diagnostics } };
187
188
  }
188
189
  }
190
+ // copied from old config --edit --test flow
191
+ async function _startReinitializeStack({ token, projectId, }) {
192
+ const auth = { token, scopeType: 'project', scopeId: projectId };
193
+ // stack id IS ST-${projectId} – it has already been checked and doesn't exist
194
+ const { confirm } = await inquirer.prompt([
195
+ {
196
+ type: 'confirm',
197
+ name: 'confirm',
198
+ message: `Do you want to create a ${chalk.blue('new')}, empty Blueprint deployment with the ${chalk.blue('existing')} configuration?`,
199
+ },
200
+ ]);
201
+ if (!confirm)
202
+ return { ok: false, error: 'Reinitialization cancelled' };
203
+ const { ok: projectOk, project, error: projectError } = await getProject(auth);
204
+ if (!projectOk) {
205
+ return {
206
+ ok: false,
207
+ error: projectError || 'Failed to find Project while creating Stack',
208
+ };
209
+ }
210
+ const projectDisplayName = project.displayName;
211
+ const response = await createStack({
212
+ stackMutation: {
213
+ name: projectDisplayName,
214
+ scopeType: 'project',
215
+ scopeId: projectId,
216
+ document: { resources: [] },
217
+ },
218
+ auth,
219
+ });
220
+ if (!response.ok)
221
+ return { ok: false, error: response.error || 'Failed to create new Stack' };
222
+ return {
223
+ ok: true,
224
+ stackId: response.stack.id,
225
+ projectDisplayName,
226
+ };
227
+ }
@@ -1,5 +1,3 @@
1
- export type { BlueprintAddOptions } from './add.js';
2
- export { blueprintAddCore } from './add.js';
3
1
  export type { BlueprintConfigOptions } from './config.js';
4
2
  export { blueprintConfigCore } from './config.js';
5
3
  export type { BlueprintDeployOptions } from './deploy.js';
@@ -1,4 +1,3 @@
1
- export { blueprintAddCore } from './add.js';
2
1
  export { blueprintConfigCore } from './config.js';
3
2
  export { blueprintDeployCore } from './deploy.js';
4
3
  export { blueprintDestroyCore } from './destroy.js';
@@ -9,6 +9,7 @@ export interface BlueprintInitOptions extends CoreConfig {
9
9
  example?: string;
10
10
  'blueprint-type'?: string;
11
11
  'project-id'?: string;
12
+ 'organization-id'?: string;
12
13
  'stack-id'?: string;
13
14
  'stack-name'?: string;
14
15
  };
@@ -16,7 +16,9 @@ const LAUNCH_LIMIT_STACK_PER_PROJECT = true;
16
16
  export async function blueprintInitCore(options) {
17
17
  const { bin = 'sanity', log, token, args, flags } = options;
18
18
  try {
19
- const { dir: flagDir, example: flagExample, 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId, 'stack-id': flagStackId, 'stack-name': flagStackName, } = flags;
19
+ const { dir: flagDir, example: flagExample, 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId,
20
+ // 'organization-id': flagOrganizationId,
21
+ 'stack-id': flagStackId, 'stack-name': flagStackName, } = flags;
20
22
  const { dir: argDir } = args;
21
23
  const providedDir = argDir || flagDir;
22
24
  const blueprintDir = providedDir || '.';
@@ -3,12 +3,12 @@ import { getStack } from '../../actions/blueprints/stacks.js';
3
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
- const { projectId, stackId, parsedBlueprint, fileInfo } = blueprint;
6
+ const { scopeType, scopeId, stackId, parsedBlueprint, fileInfo } = blueprint;
7
7
  log(`${chalk.bold.blueBright('Blueprint Deployment Plan')} ${chalk.dim(`(${fileInfo.fileName})`)}`);
8
8
  log(formatResourceTree(parsedBlueprint.resources));
9
- if (token && projectId && stackId) {
9
+ if (token && scopeType && scopeId && stackId) {
10
10
  const stackResponse = await getStack({
11
- auth: { token, scopeType: 'project', scopeId: projectId },
11
+ auth: { token, scopeType, scopeId },
12
12
  stackId,
13
13
  });
14
14
  if (!stackResponse.ok) {
@@ -5,6 +5,7 @@ export interface BlueprintStacksOptions extends CoreConfig {
5
5
  blueprint: ReadBlueprintResult;
6
6
  flags: {
7
7
  'project-id'?: string;
8
+ 'organization-id'?: string;
8
9
  };
9
10
  }
10
11
  export declare function blueprintStacksCore(options: BlueprintStacksOptions): Promise<CoreResult>;