@sanity/runtime-cli 17.7.1 → 17.8.0-blueprinit.g0993fa5
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/README.md +26 -21
- package/dist/actions/blueprinit/manifest.d.ts +34 -0
- package/dist/actions/blueprinit/manifest.js +107 -0
- package/dist/actions/blueprinit/studio.d.ts +6 -0
- package/dist/actions/blueprinit/studio.js +43 -0
- package/dist/actions/blueprinit/templates.d.ts +73 -0
- package/dist/actions/blueprinit/templates.js +138 -0
- package/dist/actions/blueprinit/workspace.d.ts +24 -0
- package/dist/actions/blueprinit/workspace.js +146 -0
- package/dist/actions/blueprints/stacks.d.ts +2 -0
- package/dist/actions/blueprints/stacks.js +1 -0
- package/dist/actions/sanity/datasets.d.ts +15 -0
- package/dist/actions/sanity/datasets.js +26 -0
- package/dist/actions/sanity/projects.d.ts +11 -0
- package/dist/actions/sanity/projects.js +48 -22
- package/dist/baseCommands.d.ts +6 -0
- package/dist/baseCommands.js +9 -1
- package/dist/commands/blueprints/stacks.d.ts +2 -1
- package/dist/commands/blueprints/stacks.js +14 -6
- package/dist/commands/init.d.ts +20 -0
- package/dist/commands/init.js +72 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +5 -0
- package/dist/cores/blueprinit/adopt.d.ts +6 -0
- package/dist/cores/blueprinit/adopt.js +154 -0
- package/dist/cores/blueprinit/finish.d.ts +15 -0
- package/dist/cores/blueprinit/finish.js +73 -0
- package/dist/cores/blueprinit/fresh.d.ts +3 -0
- package/dist/cores/blueprinit/fresh.js +173 -0
- package/dist/cores/blueprinit/preview.d.ts +24 -0
- package/dist/cores/blueprinit/preview.js +97 -0
- package/dist/cores/blueprinit/prompts.d.ts +15 -0
- package/dist/cores/blueprinit/prompts.js +43 -0
- package/dist/cores/blueprinit/selection.d.ts +15 -0
- package/dist/cores/blueprinit/selection.js +90 -0
- package/dist/cores/blueprinit/stack-name.d.ts +39 -0
- package/dist/cores/blueprinit/stack-name.js +56 -0
- package/dist/cores/blueprinit/target.d.ts +33 -0
- package/dist/cores/blueprinit/target.js +80 -0
- package/dist/cores/blueprinit/types.d.ts +18 -0
- package/dist/cores/blueprinit/types.js +1 -0
- package/dist/cores/blueprinit.d.ts +7 -0
- package/dist/cores/blueprinit.js +55 -0
- package/dist/cores/blueprints/stacks.d.ts +8 -3
- package/dist/cores/blueprints/stacks.js +282 -85
- package/dist/cores/functions/test.d.ts +2 -19
- package/dist/cores/functions/test.js +55 -111
- package/dist/server/app.js +4 -6
- package/dist/server/static/components/payload-panel.js +1 -1
- package/dist/utils/display/intro.d.ts +6 -0
- package/dist/utils/display/intro.js +63 -0
- package/dist/utils/display/prompt.d.ts +4 -1
- package/dist/utils/display/prompt.js +16 -2
- package/dist/utils/external/npmjs.d.ts +7 -0
- package/dist/utils/external/npmjs.js +10 -0
- package/dist/utils/functions/child-process-wrapper.js +18 -6
- package/dist/utils/functions/child-runtime/durable.d.ts +18 -0
- package/dist/utils/functions/child-runtime/durable.js +46 -0
- package/dist/utils/functions/invoke/local.js +26 -9
- package/dist/utils/functions/invoke/resolve-invoke-type.d.ts +51 -0
- package/dist/utils/functions/invoke/resolve-invoke-type.js +131 -0
- package/dist/utils/progress.js +6 -3
- package/dist/utils/types.d.ts +46 -2
- package/oclif.manifest.json +155 -5
- package/package.json +1 -1
- package/skills/blueprints-stacks.md +14 -2
- package/skills/init.md +170 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { select } from '@inquirer/prompts';
|
|
2
|
+
export async function chooseOrganization(groups, organizationId, interactive) {
|
|
3
|
+
if (groups.length === 0) {
|
|
4
|
+
return failure('No organizations found for this account.');
|
|
5
|
+
}
|
|
6
|
+
if (organizationId) {
|
|
7
|
+
const organization = groups.find((group) => group.organization.id === organizationId);
|
|
8
|
+
return organization
|
|
9
|
+
? { ok: true, value: organization }
|
|
10
|
+
: failure(`No organization "${organizationId}" found for this account.`);
|
|
11
|
+
}
|
|
12
|
+
if (groups.length === 1)
|
|
13
|
+
return { ok: true, value: groups[0] };
|
|
14
|
+
if (!interactive) {
|
|
15
|
+
return failure('Multiple organizations found.', ['Pass --organization-id <id> to choose one.']);
|
|
16
|
+
}
|
|
17
|
+
const selectedId = await select({
|
|
18
|
+
message: 'Choose a Sanity organization:',
|
|
19
|
+
choices: groups.map(({ organization, projects }) => ({
|
|
20
|
+
name: organization.name,
|
|
21
|
+
value: organization.id,
|
|
22
|
+
description: `${projects?.length ?? 0} projects`,
|
|
23
|
+
})),
|
|
24
|
+
});
|
|
25
|
+
const selected = groups.find(({ organization }) => organization.id === selectedId);
|
|
26
|
+
return selected ? { ok: true, value: selected } : failure('Organization not found.');
|
|
27
|
+
}
|
|
28
|
+
export async function chooseProject(projects, projectId, organizationName, interactive) {
|
|
29
|
+
if (projects.length === 0) {
|
|
30
|
+
return failure(`No projects found in ${organizationName}.`);
|
|
31
|
+
}
|
|
32
|
+
if (projectId) {
|
|
33
|
+
const project = projects.find(({ id }) => id === projectId);
|
|
34
|
+
return project
|
|
35
|
+
? { ok: true, value: project }
|
|
36
|
+
: failure(`No project "${projectId}" found in ${organizationName}.`);
|
|
37
|
+
}
|
|
38
|
+
if (projects.length === 1)
|
|
39
|
+
return { ok: true, value: projects[0] };
|
|
40
|
+
if (!interactive) {
|
|
41
|
+
return failure('Multiple projects found.', ['Pass --project-id <id> to choose one.']);
|
|
42
|
+
}
|
|
43
|
+
const selectedId = await select({
|
|
44
|
+
message: 'Choose a Sanity project:',
|
|
45
|
+
choices: projects.map((project) => ({ name: project.displayName, value: project.id })),
|
|
46
|
+
});
|
|
47
|
+
const selected = projects.find(({ id }) => id === selectedId);
|
|
48
|
+
return selected ? { ok: true, value: selected } : failure('Project not found.');
|
|
49
|
+
}
|
|
50
|
+
export async function chooseDataset(datasets, datasetName, projectName, interactive) {
|
|
51
|
+
if (datasets.length === 0) {
|
|
52
|
+
return failure(`No datasets found in ${projectName}.`);
|
|
53
|
+
}
|
|
54
|
+
if (datasetName) {
|
|
55
|
+
const dataset = datasets.find(({ name }) => name === datasetName);
|
|
56
|
+
return dataset
|
|
57
|
+
? { ok: true, value: dataset }
|
|
58
|
+
: failure(`No dataset "${datasetName}" found in ${projectName}.`);
|
|
59
|
+
}
|
|
60
|
+
if (datasets.length === 1)
|
|
61
|
+
return { ok: true, value: datasets[0] };
|
|
62
|
+
if (!interactive) {
|
|
63
|
+
return failure('Multiple datasets found.', ['Pass --dataset <name> to choose one.']);
|
|
64
|
+
}
|
|
65
|
+
const production = datasets.find(({ name }) => name === 'production');
|
|
66
|
+
const orderedDatasets = production
|
|
67
|
+
? [production, ...datasets.filter(({ name }) => name !== 'production')]
|
|
68
|
+
: datasets;
|
|
69
|
+
const selectedName = await select({
|
|
70
|
+
message: 'Choose a Sanity dataset:',
|
|
71
|
+
choices: orderedDatasets.map((dataset) => ({
|
|
72
|
+
name: dataset.name,
|
|
73
|
+
value: dataset.name,
|
|
74
|
+
description: dataset.aclMode,
|
|
75
|
+
})),
|
|
76
|
+
});
|
|
77
|
+
const selected = datasets.find(({ name }) => name === selectedName);
|
|
78
|
+
return selected ? { ok: true, value: selected } : failure('Dataset not found.');
|
|
79
|
+
}
|
|
80
|
+
export function findProject(groups, projectId) {
|
|
81
|
+
for (const { organization, projects = [] } of groups) {
|
|
82
|
+
const project = projects.find(({ id }) => id === projectId);
|
|
83
|
+
if (project)
|
|
84
|
+
return { organization, project };
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
function failure(message, suggestions) {
|
|
89
|
+
return { ok: false, error: { message, suggestions } };
|
|
90
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Logger } from '../../utils/logger.js';
|
|
2
|
+
import type { BlueprinitFlags } from './types.js';
|
|
3
|
+
interface TakenNamesOptions {
|
|
4
|
+
token: string;
|
|
5
|
+
organizationId: string;
|
|
6
|
+
log: Logger;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Stack names already used in the organization.
|
|
10
|
+
*
|
|
11
|
+
* Returns nothing when the listing fails. A token that cannot read Stacks is not a reason
|
|
12
|
+
* to stop, because the create call reports the same conflict a moment later.
|
|
13
|
+
*/
|
|
14
|
+
export declare function takenStackNames(options: TakenNamesOptions): Promise<Set<string>>;
|
|
15
|
+
interface ChooseStackNameOptions {
|
|
16
|
+
flags: BlueprinitFlags;
|
|
17
|
+
interactive: boolean;
|
|
18
|
+
defaultName: string;
|
|
19
|
+
taken: Set<string>;
|
|
20
|
+
log: Logger;
|
|
21
|
+
}
|
|
22
|
+
type ChooseStackNameResult = {
|
|
23
|
+
ok: true;
|
|
24
|
+
value: string;
|
|
25
|
+
} | {
|
|
26
|
+
ok: false;
|
|
27
|
+
error: string;
|
|
28
|
+
suggestions: string[];
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* A Stack name the organization does not already hold.
|
|
32
|
+
*
|
|
33
|
+
* Asks again rather than failing, because the conflict is only visible after `init` has
|
|
34
|
+
* written every local file.
|
|
35
|
+
*/
|
|
36
|
+
export declare function chooseStackName(options: ChooseStackNameOptions): Promise<ChooseStackNameResult>;
|
|
37
|
+
/** `field-notes-production`, then `field-notes-production-2`, until one is free. */
|
|
38
|
+
export declare function nextFreeName(name: string, taken: Set<string>): string;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { listStacks } from '../../actions/blueprints/stacks.js';
|
|
2
|
+
import { warn } from '../../utils/display/presenters.js';
|
|
3
|
+
import { promptForStackName } from './prompts.js';
|
|
4
|
+
/**
|
|
5
|
+
* Stack names already used in the organization.
|
|
6
|
+
*
|
|
7
|
+
* Returns nothing when the listing fails. A token that cannot read Stacks is not a reason
|
|
8
|
+
* to stop, because the create call reports the same conflict a moment later.
|
|
9
|
+
*/
|
|
10
|
+
export async function takenStackNames(options) {
|
|
11
|
+
const { token, organizationId, log } = options;
|
|
12
|
+
const result = await listStacks({ token, scopeType: 'organization', scopeId: organizationId }, log);
|
|
13
|
+
if (!result.ok) {
|
|
14
|
+
log.verbose(`Could not list Stacks to check the name: ${result.error}`);
|
|
15
|
+
return new Set();
|
|
16
|
+
}
|
|
17
|
+
return new Set(result.stacks.map(({ name }) => name));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A Stack name the organization does not already hold.
|
|
21
|
+
*
|
|
22
|
+
* Asks again rather than failing, because the conflict is only visible after `init` has
|
|
23
|
+
* written every local file.
|
|
24
|
+
*/
|
|
25
|
+
export async function chooseStackName(options) {
|
|
26
|
+
const { flags, interactive, defaultName, taken, log } = options;
|
|
27
|
+
const suggested = nextFreeName(defaultName, taken);
|
|
28
|
+
if (suggested !== defaultName) {
|
|
29
|
+
log(warn(`A Stack named "${defaultName}" already exists. Suggesting "${suggested}".`));
|
|
30
|
+
}
|
|
31
|
+
let name = await promptForStackName(flags, interactive, suggested);
|
|
32
|
+
while (taken.has(name)) {
|
|
33
|
+
// Only a --stack-name value can be taken here; every other path starts from a free name.
|
|
34
|
+
if (!interactive || flags.yes || flags.stackName) {
|
|
35
|
+
return {
|
|
36
|
+
ok: false,
|
|
37
|
+
error: `A Stack named "${name}" already exists in this organization.`,
|
|
38
|
+
suggestions: [`Pass --stack-name with a different value, such as ${suggested}.`],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
log(warn(`A Stack named "${name}" already exists in this organization.`));
|
|
42
|
+
name = await promptForStackName({}, interactive, nextFreeName(name, taken));
|
|
43
|
+
}
|
|
44
|
+
return { ok: true, value: name };
|
|
45
|
+
}
|
|
46
|
+
/** `field-notes-production`, then `field-notes-production-2`, until one is free. */
|
|
47
|
+
export function nextFreeName(name, taken) {
|
|
48
|
+
if (!taken.has(name))
|
|
49
|
+
return name;
|
|
50
|
+
for (let suffix = 2; suffix < 100; suffix += 1) {
|
|
51
|
+
const candidate = `${name}-${suffix}`;
|
|
52
|
+
if (!taken.has(candidate))
|
|
53
|
+
return candidate;
|
|
54
|
+
}
|
|
55
|
+
return name;
|
|
56
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Result } from '../../utils/types.js';
|
|
2
|
+
interface TargetError {
|
|
3
|
+
message: string;
|
|
4
|
+
suggestions?: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function isEmptyDirectory(dir: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* What the directory prompt offers.
|
|
9
|
+
*
|
|
10
|
+
* The current directory when it is free, and a new directory named for the project when it
|
|
11
|
+
* is not, so accepting the default never writes a scaffold beside unrelated files.
|
|
12
|
+
*/
|
|
13
|
+
export declare function defaultTargetPath(dir: string, baseName: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Where to scaffold, asked rather than assumed.
|
|
16
|
+
*
|
|
17
|
+
* An unattended run keeps the current directory. Relocating a scaffold with nobody watching
|
|
18
|
+
* would move files a script expects to find where it started.
|
|
19
|
+
*/
|
|
20
|
+
export declare function promptForTargetDirectory(dir: string, baseName: string, interactive: boolean): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the answer against the starting directory and refuse a target that is already
|
|
23
|
+
* something else: a file, a Blueprint, or a Studio that adoption should handle instead.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveTargetDirectory(dir: string, answer: string, bin: string): Result<string, TargetError>;
|
|
26
|
+
/**
|
|
27
|
+
* The `cd` the printed next steps need, or nothing when the files landed here.
|
|
28
|
+
*
|
|
29
|
+
* Measured from the process working directory, not from the flow's starting directory,
|
|
30
|
+
* because a directory argument moves the target without moving the reader.
|
|
31
|
+
*/
|
|
32
|
+
export declare function relativeTargetPath(target: string, from?: string): string | undefined;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
3
|
+
import { cwd } from 'node:process';
|
|
4
|
+
import { input } from '@inquirer/prompts';
|
|
5
|
+
import { detectStudio } from '../../actions/blueprinit/studio.js';
|
|
6
|
+
import { findBlueprintFile } from '../../actions/blueprints/blueprint.js';
|
|
7
|
+
/** Entries that say nothing about whether a directory is in use. */
|
|
8
|
+
const IGNORED_ENTRIES = new Set(['.git', '.DS_Store']);
|
|
9
|
+
export function isEmptyDirectory(dir) {
|
|
10
|
+
try {
|
|
11
|
+
return readdirSync(dir).every((entry) => IGNORED_ENTRIES.has(entry));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// A directory that does not exist yet has nothing to overwrite.
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* What the directory prompt offers.
|
|
20
|
+
*
|
|
21
|
+
* The current directory when it is free, and a new directory named for the project when it
|
|
22
|
+
* is not, so accepting the default never writes a scaffold beside unrelated files.
|
|
23
|
+
*/
|
|
24
|
+
export function defaultTargetPath(dir, baseName) {
|
|
25
|
+
return isEmptyDirectory(dir) ? '.' : `./${baseName}`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Where to scaffold, asked rather than assumed.
|
|
29
|
+
*
|
|
30
|
+
* An unattended run keeps the current directory. Relocating a scaffold with nobody watching
|
|
31
|
+
* would move files a script expects to find where it started.
|
|
32
|
+
*/
|
|
33
|
+
export async function promptForTargetDirectory(dir, baseName, interactive) {
|
|
34
|
+
if (!interactive)
|
|
35
|
+
return '.';
|
|
36
|
+
const fallback = defaultTargetPath(dir, baseName);
|
|
37
|
+
const answer = await input({ message: 'Where should the files go?', default: fallback });
|
|
38
|
+
return answer.trim() || fallback;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the answer against the starting directory and refuse a target that is already
|
|
42
|
+
* something else: a file, a Blueprint, or a Studio that adoption should handle instead.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveTargetDirectory(dir, answer, bin) {
|
|
45
|
+
const target = isAbsolute(answer) ? answer : resolve(dir, answer);
|
|
46
|
+
if (existsSync(target) && !statSync(target).isDirectory()) {
|
|
47
|
+
return { ok: false, error: { message: `${answer} is not a directory.` } };
|
|
48
|
+
}
|
|
49
|
+
const blueprint = findBlueprintFile(target);
|
|
50
|
+
if (blueprint) {
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
error: {
|
|
54
|
+
message: `A Blueprint already exists in ${answer} (${blueprint.fileName}).`,
|
|
55
|
+
suggestions: [`Review it with: npx ${bin} blueprints doctor`],
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const studio = detectStudio(target);
|
|
60
|
+
if (studio) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
error: {
|
|
64
|
+
message: `${answer} already holds a Studio (${studio.configFile}).`,
|
|
65
|
+
suggestions: [`Wrap it in a Blueprint with: npx ${bin} init ${answer}`],
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return { ok: true, value: target };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The `cd` the printed next steps need, or nothing when the files landed here.
|
|
73
|
+
*
|
|
74
|
+
* Measured from the process working directory, not from the flow's starting directory,
|
|
75
|
+
* because a directory argument moves the target without moving the reader.
|
|
76
|
+
*/
|
|
77
|
+
export function relativeTargetPath(target, from = cwd()) {
|
|
78
|
+
const path = relative(from, target);
|
|
79
|
+
return path === '' ? undefined : path;
|
|
80
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Logger } from '../../utils/logger.js';
|
|
2
|
+
export interface BlueprinitFlags {
|
|
3
|
+
yes?: boolean;
|
|
4
|
+
organizationId?: string;
|
|
5
|
+
projectId?: string;
|
|
6
|
+
datasetName?: string;
|
|
7
|
+
stackName?: string;
|
|
8
|
+
studioName?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface BlueprinitFlowOptions {
|
|
11
|
+
bin: string;
|
|
12
|
+
dir: string;
|
|
13
|
+
/** True when a directory argument named the target, so the flow must not ask for one. */
|
|
14
|
+
targetProvided: boolean;
|
|
15
|
+
flags: BlueprinitFlags;
|
|
16
|
+
log: Logger;
|
|
17
|
+
token: string;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BlueprinitFlags } from './blueprinit/types.js';
|
|
2
|
+
import type { CoreConfig, CoreResult } from './index.js';
|
|
3
|
+
export interface BlueprinitOptions extends CoreConfig {
|
|
4
|
+
dir?: string;
|
|
5
|
+
flags?: BlueprinitFlags;
|
|
6
|
+
}
|
|
7
|
+
export declare function blueprinitCore(options: BlueprinitOptions): Promise<CoreResult>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { cwd } from 'node:process';
|
|
2
|
+
import { detectStudio } from '../actions/blueprinit/studio.js';
|
|
3
|
+
import { findBlueprintFile } from '../actions/blueprints/blueprint.js';
|
|
4
|
+
import { readConfigFile } from '../actions/blueprints/config.js';
|
|
5
|
+
import { validTokenOrErrorMessage } from '../utils/validated-token.js';
|
|
6
|
+
import { adoptFlow } from './blueprinit/adopt.js';
|
|
7
|
+
import { freshFlow } from './blueprinit/fresh.js';
|
|
8
|
+
export async function blueprinitCore(options) {
|
|
9
|
+
const { bin, log, flags = {}, dir = cwd() } = options;
|
|
10
|
+
try {
|
|
11
|
+
const existingBlueprint = findBlueprintFile(dir);
|
|
12
|
+
if (existingBlueprint) {
|
|
13
|
+
const blueprintConfig = readConfigFile(existingBlueprint.blueprintFilePath);
|
|
14
|
+
if (!blueprintConfig?.stackId) {
|
|
15
|
+
return {
|
|
16
|
+
success: false,
|
|
17
|
+
error: `A Blueprint exists here (${existingBlueprint.fileName}), but it is not connected to a Stack.`,
|
|
18
|
+
suggestions: [`Finish setup with: npx ${bin} blueprints init`],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
log(`A blueprint already exists here (${existingBlueprint.fileName}).`);
|
|
22
|
+
log('');
|
|
23
|
+
log('Use these instead:');
|
|
24
|
+
log(` npx ${bin} blueprints config --edit`);
|
|
25
|
+
log(` npx ${bin} blueprints doctor`);
|
|
26
|
+
return {
|
|
27
|
+
success: true,
|
|
28
|
+
json: { alreadyInitialized: true, blueprintFile: existingBlueprint.fileName },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const tokenCheck = await validTokenOrErrorMessage(log);
|
|
32
|
+
if (!tokenCheck.ok) {
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
error: 'No Sanity session found.',
|
|
36
|
+
suggestions: ['Sign in first: npx sanity login', 'Then run init again.'],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
log('Setting up your Sanity workspace.');
|
|
40
|
+
log('');
|
|
41
|
+
const flowOptions = {
|
|
42
|
+
bin,
|
|
43
|
+
dir,
|
|
44
|
+
targetProvided: Boolean(options.dir),
|
|
45
|
+
flags,
|
|
46
|
+
log,
|
|
47
|
+
token: tokenCheck.value,
|
|
48
|
+
};
|
|
49
|
+
const studio = detectStudio(dir);
|
|
50
|
+
return studio ? await adoptFlow({ ...flowOptions, studio }) : await freshFlow(flowOptions);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -2,13 +2,18 @@ import type { ScopeType } from '../../utils/types.js';
|
|
|
2
2
|
import type { CoreConfig, CoreResult } from '../index.js';
|
|
3
3
|
export interface BlueprintStacksOptions extends CoreConfig {
|
|
4
4
|
token: string;
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Scope to list Stacks for, resolved by the base command (flags > env > config).
|
|
7
|
+
* Prompted for when absent.
|
|
8
|
+
*/
|
|
9
|
+
scopeType?: ScopeType;
|
|
10
|
+
scopeId?: string;
|
|
8
11
|
/** Locally-configured Stack ID, used only to highlight the current Stack in listings. */
|
|
9
12
|
localStackId?: string;
|
|
10
13
|
flags: {
|
|
14
|
+
all?: boolean;
|
|
11
15
|
'include-projects'?: boolean;
|
|
16
|
+
json?: boolean;
|
|
12
17
|
verbose?: boolean;
|
|
13
18
|
};
|
|
14
19
|
}
|