@sanity/runtime-cli 17.3.0-blueprinit.gb1c5e1e → 17.3.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.
@@ -1,142 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { dirname, join, posix } from 'node:path';
3
- import { renderBlueprint, STARTER_FUNCTION_NAME } from './manifest.js';
4
- import { FUNCTION_SET_SLUG, functionPackageJson, renderStudioConfig, STUDIO_POST_TYPE, STUDIO_SCHEMA_TYPES, studioPackageJson, } from './templates.js';
5
- const GITIGNORE_ENTRY = '# Sanity Blueprints\n.sanity/';
6
- const DEFAULT_GITIGNORE = `node_modules
7
- .env
8
-
9
- ${GITIGNORE_ENTRY}
10
- `;
11
- export function scaffoldWorkspace(options) {
12
- const { dir, studioName } = options;
13
- assertTargetsAvailable(dir, [
14
- posix.join('studios', studioName),
15
- posix.join('functions', STARTER_FUNCTION_NAME),
16
- ]);
17
- const packageJson = preparePackageJson(options, ['studios/*', 'functions/*']);
18
- const files = [];
19
- const write = createWriter(dir, files);
20
- const blueprintFilePath = write('sanity.blueprint.ts', renderBlueprint({
21
- ...manifestOptions(options),
22
- studioSrc: `./studios/${studioName}`,
23
- withFunction: true,
24
- }));
25
- write(posix.join('studios', studioName, 'package.json'), formatJson(studioPackageJson(studioName)));
26
- write(posix.join('studios', studioName, 'sanity.config.ts'), renderStudioConfig(options));
27
- write(posix.join('studios', studioName, 'schemaTypes', 'index.ts'), STUDIO_SCHEMA_TYPES);
28
- write(posix.join('studios', studioName, 'schemaTypes', 'post.ts'), STUDIO_POST_TYPE);
29
- write(posix.join('studios', studioName, 'static', '.gitkeep'), '');
30
- write(posix.join('functions', STARTER_FUNCTION_NAME, 'package.json'), formatJson(functionPackageJson(STARTER_FUNCTION_NAME)));
31
- write(posix.join('functions', STARTER_FUNCTION_NAME, 'index.ts'), FUNCTION_SET_SLUG);
32
- writePreparedRootFiles(dir, packageJson, files);
33
- return { blueprintFilePath, files };
34
- }
35
- export function adoptWorkspace(options) {
36
- const { dir } = options;
37
- const packageJson = preparePackageJson(options, []);
38
- const files = [];
39
- const write = createWriter(dir, files);
40
- const blueprintFilePath = write('sanity.blueprint.ts', renderBlueprint({ ...manifestOptions(options), studioSrc: '.', withFunction: false }));
41
- writePreparedRootFiles(dir, packageJson, files);
42
- return { blueprintFilePath, files };
43
- }
44
- function manifestOptions(options) {
45
- return {
46
- projectName: options.stackName,
47
- displayName: options.displayName,
48
- projectId: options.projectId,
49
- datasetName: options.datasetName,
50
- studioName: options.studioName,
51
- };
52
- }
53
- function assertTargetsAvailable(dir, paths) {
54
- const conflicts = paths.filter((path) => existsSync(join(dir, path)));
55
- if (conflicts.length > 0) {
56
- throw new Error(`Refusing to overwrite existing paths: ${conflicts.join(', ')}`);
57
- }
58
- }
59
- function createWriter(dir, files) {
60
- return (relativePath, content) => {
61
- const filePath = join(dir, relativePath);
62
- mkdirSync(dirname(filePath), { recursive: true });
63
- writeFileSync(filePath, content);
64
- files.push(relativePath);
65
- return filePath;
66
- };
67
- }
68
- function writePreparedRootFiles(dir, packageJson, files) {
69
- if (writeGitignore(dir))
70
- files.push('.gitignore');
71
- writeFileSync(join(dir, 'package.json'), formatJson(packageJson));
72
- files.push('package.json');
73
- }
74
- function writeGitignore(dir) {
75
- const filePath = join(dir, '.gitignore');
76
- if (!existsSync(filePath)) {
77
- writeFileSync(filePath, DEFAULT_GITIGNORE);
78
- return true;
79
- }
80
- const existing = readFileSync(filePath, 'utf8');
81
- if (existing.split(/\r?\n/).some((line) => line.trim() === '.sanity/'))
82
- return false;
83
- writeFileSync(filePath, `${existing.trimEnd()}\n\n${GITIGNORE_ENTRY}\n`);
84
- return true;
85
- }
86
- function preparePackageJson(options, requiredWorkspaces) {
87
- const filePath = join(options.dir, 'package.json');
88
- const parsed = existsSync(filePath)
89
- ? JSON.parse(readFileSync(filePath, 'utf8'))
90
- : {
91
- name: options.stackName,
92
- version: '0.1.0',
93
- private: true,
94
- type: 'module',
95
- };
96
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
97
- throw new Error('The existing package.json must contain a JSON object.');
98
- }
99
- const packageJson = parsed;
100
- addWorkspaces(packageJson, requiredWorkspaces);
101
- addBlueprintsDependency(packageJson, options.blueprintsVersion);
102
- return packageJson;
103
- }
104
- function addWorkspaces(packageJson, required) {
105
- if (required.length === 0)
106
- return;
107
- if (!packageJson.workspaces) {
108
- packageJson.workspaces = required;
109
- return;
110
- }
111
- if (Array.isArray(packageJson.workspaces)) {
112
- packageJson.workspaces = [...new Set([...packageJson.workspaces, ...required])];
113
- return;
114
- }
115
- if (!packageJson.workspaces || typeof packageJson.workspaces !== 'object') {
116
- throw new Error('Cannot add generated packages to the existing package.json workspaces.');
117
- }
118
- const workspaces = packageJson.workspaces;
119
- if (Array.isArray(workspaces.packages)) {
120
- workspaces.packages = [...new Set([...workspaces.packages, ...required])];
121
- return;
122
- }
123
- throw new Error('Cannot add generated packages to the existing package.json workspaces.');
124
- }
125
- function addBlueprintsDependency(packageJson, blueprintsVersion) {
126
- const dependencies = recordOrEmpty(packageJson.dependencies);
127
- const devDependencies = recordOrEmpty(packageJson.devDependencies);
128
- if (dependencies['@sanity/blueprints'] || devDependencies['@sanity/blueprints'])
129
- return;
130
- packageJson.devDependencies = {
131
- ...devDependencies,
132
- '@sanity/blueprints': blueprintsVersion,
133
- };
134
- }
135
- function recordOrEmpty(value) {
136
- return value && typeof value === 'object' && !Array.isArray(value)
137
- ? value
138
- : {};
139
- }
140
- function formatJson(value) {
141
- return `${JSON.stringify(value, null, 2)}\n`;
142
- }
@@ -1,15 +0,0 @@
1
- import type { Logger } from '../../utils/logger.js';
2
- import type { ActionResponse } from '../../utils/types.js';
3
- export interface Dataset {
4
- name: string;
5
- aclMode: string;
6
- }
7
- interface ListDatasetsResponse extends ActionResponse {
8
- datasets: Dataset[];
9
- }
10
- export declare function listDatasets({ token, projectId, logger, }: {
11
- token: string;
12
- projectId: string;
13
- logger: Logger;
14
- }): Promise<ListDatasetsResponse>;
15
- export {};
@@ -1,26 +0,0 @@
1
- import { createTracedFetch } from '../../utils/traced-fetch.js';
2
- import { projectsApiPath } from './projects.js';
3
- export async function listDatasets({ token, projectId, logger, }) {
4
- const fetchFn = createTracedFetch(logger);
5
- const url = `${projectsApiPath}/${encodeURIComponent(projectId)}/datasets`;
6
- const response = await fetchFn(url, {
7
- method: 'GET',
8
- headers: {
9
- Authorization: `Bearer ${token}`,
10
- 'Content-Type': 'application/json',
11
- },
12
- });
13
- const body = await response.json();
14
- if (!response.ok) {
15
- return {
16
- ok: false,
17
- error: body.error?.message ?? response.statusText,
18
- datasets: [],
19
- };
20
- }
21
- return {
22
- ok: true,
23
- error: null,
24
- datasets: body,
25
- };
26
- }
@@ -1,14 +0,0 @@
1
- import { RuntimeCommand } from '../baseCommands.js';
2
- export default class BlueprinitCommand extends RuntimeCommand<typeof BlueprinitCommand> {
3
- static hidden: boolean;
4
- static summary: string;
5
- static description: string;
6
- static examples: string[];
7
- static flags: {
8
- llms: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
- yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
- 'organization-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
- 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- };
13
- run(): Promise<Record<string, unknown> | undefined>;
14
- }
@@ -1,46 +0,0 @@
1
- import { Flags } from '@oclif/core';
2
- import { llmsFlagConfig } from '@sanity-labs/oclif-plugin-skills-flag';
3
- import { RuntimeCommand } from '../baseCommands.js';
4
- import { blueprinitCore } from '../cores/blueprinit.js';
5
- import { Logger } from '../utils/logger.js';
6
- export default class BlueprinitCommand extends RuntimeCommand {
7
- static hidden = true;
8
- static summary = 'Experimental blueprints-first project init';
9
- static description = `Scaffolds a Blueprint, Studio, and starter function around an existing Sanity project.
10
-
11
- This hidden command is an experiment; its interface and generated files are not stable.`;
12
- static examples = [
13
- '<%= config.bin %> <%= command.id %>',
14
- '<%= config.bin %> <%= command.id %> --yes',
15
- ];
16
- static flags = {
17
- llms: Flags.boolean(llmsFlagConfig),
18
- yes: Flags.boolean({
19
- char: 'y',
20
- description: 'Skip confirmation prompts',
21
- default: false,
22
- }),
23
- 'organization-id': Flags.string({
24
- description: 'Organization to use when the account has several',
25
- aliases: ['organization', 'organizationId', 'org'],
26
- }),
27
- 'project-id': Flags.string({
28
- description: 'Project to use when the organization has several',
29
- aliases: ['project', 'projectId'],
30
- }),
31
- };
32
- async run() {
33
- const result = await blueprinitCore({
34
- bin: this.config.bin,
35
- log: Logger(this.log.bind(this), this.flags),
36
- flags: {
37
- yes: this.flags.yes,
38
- organizationId: this.flags['organization-id'],
39
- projectId: this.flags['project-id'],
40
- },
41
- });
42
- if (!result.success)
43
- this.coreError(result);
44
- return result.json;
45
- }
46
- }
@@ -1,6 +0,0 @@
1
- import type { DetectedStudio } from '../../actions/blueprinit/studio.js';
2
- import type { CoreResult } from '../index.js';
3
- import type { BlueprinitFlowOptions } from './types.js';
4
- export declare function adoptFlow(options: BlueprinitFlowOptions & {
5
- studio: DetectedStudio;
6
- }): Promise<CoreResult>;
@@ -1,90 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { slugify } from '../../actions/blueprinit/manifest.js';
4
- import { adoptWorkspace } from '../../actions/blueprinit/workspace.js';
5
- import { listDatasets } from '../../actions/sanity/datasets.js';
6
- import { groupProjectsByOrganization } from '../../actions/sanity/projects.js';
7
- import { check } from '../../utils/display/presenters.js';
8
- import { getLatestNpmVersion } from '../../utils/external/npmjs.js';
9
- import { isInteractive } from '../../utils/interactive.js';
10
- import { createStackAndFinish } from './finish.js';
11
- import { confirmUnlessSkipped, promptForStudioName } from './prompts.js';
12
- import { findProject } from './selection.js';
13
- export async function adoptFlow(options) {
14
- const { bin, dir, flags, log, studio, token } = options;
15
- if (!studio.projectId || !studio.dataset) {
16
- return {
17
- success: false,
18
- error: `Found ${studio.configFile}, but could not read a projectId and dataset from it.`,
19
- suggestions: ['Use literal projectId and dataset values in the Studio config.'],
20
- };
21
- }
22
- if (flags.projectId && flags.projectId !== studio.projectId) {
23
- return { success: false, error: `The Studio uses project "${studio.projectId}".` };
24
- }
25
- log(check(`Studio detected (${studio.configFile})`));
26
- const grouped = await groupProjectsByOrganization({ token, logger: log });
27
- if (!grouped.ok) {
28
- return { success: false, error: `Could not list organizations: ${grouped.error}` };
29
- }
30
- const match = findProject(grouped.organizations, studio.projectId);
31
- if (!match) {
32
- return {
33
- success: false,
34
- error: `Project "${studio.projectId}" from ${studio.configFile} was not found in your account.`,
35
- };
36
- }
37
- if (flags.organizationId && flags.organizationId !== match.organization.id) {
38
- return {
39
- success: false,
40
- error: `Project "${studio.projectId}" belongs to organization "${match.organization.id}".`,
41
- };
42
- }
43
- const datasets = await listDatasets({ token, projectId: match.project.id, logger: log });
44
- if (!datasets.ok) {
45
- return { success: false, error: `Could not list datasets: ${datasets.error}` };
46
- }
47
- if (!datasets.datasets.some(({ name }) => name === studio.dataset)) {
48
- return {
49
- success: false,
50
- error: `Dataset "${studio.dataset}" was not found in project ${match.project.displayName}.`,
51
- };
52
- }
53
- log(check(`Matches project ${match.project.displayName} (${match.project.id}), dataset ${studio.dataset}`));
54
- log('');
55
- const confirmed = await confirmUnlessSkipped(flags, isInteractive, 'Adopt this Studio into a Blueprint?');
56
- if (!confirmed)
57
- return { success: false, error: 'Initialization cancelled.' };
58
- const studioName = await promptForStudioName(flags, isInteractive);
59
- const stackName = slugify(match.project.displayName) || match.project.id;
60
- const marker = (file) => (existsSync(join(dir, file)) ? '~' : '+');
61
- log('Changes:');
62
- log(" + sanity.blueprint.ts attach project + dataset; adopt Studio at '.'");
63
- log(` + .sanity/blueprint.config.json org scope, stack ${stackName}`);
64
- log(` ${marker('.gitignore')} .gitignore`);
65
- log(` ${marker('package.json')} package.json`);
66
- log('');
67
- const adoption = adoptWorkspace({
68
- dir,
69
- projectId: match.project.id,
70
- displayName: match.project.displayName,
71
- datasetName: studio.dataset,
72
- stackName,
73
- studioName,
74
- blueprintsVersion: await getLatestNpmVersion('@sanity/blueprints', log),
75
- });
76
- return await createStackAndFinish({
77
- ...options,
78
- organizationId: match.organization.id,
79
- projectId: match.project.id,
80
- datasetName: studio.dataset,
81
- stackName,
82
- blueprintFilePath: adoption.blueprintFilePath,
83
- files: adoption.files,
84
- nextSteps: [
85
- ` npx ${bin} blueprints plan`,
86
- ` npx ${bin} blueprints deploy`,
87
- ` npx ${bin} blueprints info`,
88
- ],
89
- });
90
- }
@@ -1,14 +0,0 @@
1
- import type { CoreResult } from '../index.js';
2
- import type { BlueprinitFlowOptions } from './types.js';
3
- interface FinishOptions extends BlueprinitFlowOptions {
4
- organizationId: string;
5
- projectId: string;
6
- datasetName: string;
7
- stackName: string;
8
- blueprintFilePath: string;
9
- files: string[];
10
- nextSteps: string[];
11
- }
12
- export declare function createStackAndFinish(options: FinishOptions): Promise<CoreResult>;
13
- export declare function alignSteps(steps: Array<[command: string, description?: string]>): string[];
14
- export {};
@@ -1,36 +0,0 @@
1
- import { writeConfigFile } from '../../actions/blueprints/config.js';
2
- import { createEmptyStack } from '../../actions/blueprints/stacks.js';
3
- import { check } from '../../utils/display/presenters.js';
4
- export async function createStackAndFinish(options) {
5
- const stack = await createEmptyStack({
6
- token: options.token,
7
- scopeType: 'organization',
8
- scopeId: options.organizationId,
9
- name: options.stackName,
10
- logger: options.log,
11
- });
12
- writeConfigFile(options.blueprintFilePath, {
13
- organizationId: options.organizationId,
14
- stackId: stack.id,
15
- });
16
- options.log(check(`Stack ${stack.name} created (org scope)`));
17
- options.log('');
18
- options.log('Next steps:');
19
- for (const step of options.nextSteps)
20
- options.log(step);
21
- return {
22
- success: true,
23
- json: {
24
- organizationId: options.organizationId,
25
- projectId: options.projectId,
26
- dataset: options.datasetName,
27
- stackId: stack.id,
28
- stackName: stack.name,
29
- files: [...options.files, '.sanity/blueprint.config.json'],
30
- },
31
- };
32
- }
33
- export function alignSteps(steps) {
34
- const width = Math.max(...steps.map(([command]) => command.length));
35
- return steps.map(([command, description]) => description ? ` ${command.padEnd(width + 2)}${description}` : ` ${command}`);
36
- }
@@ -1,3 +0,0 @@
1
- import type { CoreResult } from '../index.js';
2
- import type { BlueprinitFlowOptions } from './types.js';
3
- export declare function freshFlow(options: BlueprinitFlowOptions): Promise<CoreResult>;
@@ -1,88 +0,0 @@
1
- import { STARTER_FUNCTION_NAME, slugify } from '../../actions/blueprinit/manifest.js';
2
- import { scaffoldWorkspace } from '../../actions/blueprinit/workspace.js';
3
- import { listDatasets } from '../../actions/sanity/datasets.js';
4
- import { groupProjectsByOrganization } from '../../actions/sanity/projects.js';
5
- import { check } from '../../utils/display/presenters.js';
6
- import { getLatestNpmVersion } from '../../utils/external/npmjs.js';
7
- import { isInteractive } from '../../utils/interactive.js';
8
- import { alignSteps, createStackAndFinish } from './finish.js';
9
- import { confirmUnlessSkipped, promptForStudioName } from './prompts.js';
10
- import { chooseDataset, chooseOrganization, chooseProject } from './selection.js';
11
- export async function freshFlow(options) {
12
- const { bin, dir, flags, log, token } = options;
13
- const grouped = await groupProjectsByOrganization({ token, logger: log });
14
- if (!grouped.ok) {
15
- return { success: false, error: `Could not list organizations: ${grouped.error}` };
16
- }
17
- const organizationResult = await chooseOrganization(grouped.organizations, flags.organizationId, isInteractive);
18
- if (!organizationResult.ok) {
19
- return {
20
- success: false,
21
- error: organizationResult.error.message,
22
- suggestions: organizationResult.error.suggestions,
23
- };
24
- }
25
- const { organization, projects = [] } = organizationResult.value;
26
- const projectResult = await chooseProject(projects, flags.projectId, organization.name, isInteractive);
27
- if (!projectResult.ok) {
28
- return {
29
- success: false,
30
- error: projectResult.error.message,
31
- suggestions: projectResult.error.suggestions,
32
- };
33
- }
34
- const project = projectResult.value;
35
- const datasets = await listDatasets({ token, projectId: project.id, logger: log });
36
- if (!datasets.ok) {
37
- return { success: false, error: `Could not list datasets: ${datasets.error}` };
38
- }
39
- const dataset = chooseDataset(datasets.datasets);
40
- if (!dataset)
41
- return { success: false, error: `No datasets found in ${project.displayName}.` };
42
- log(check(`Org: ${organization.name} (${organization.id})`));
43
- log(check(`Project: ${project.displayName} (${project.id}), dataset: ${dataset.name}`));
44
- log('');
45
- const confirmed = await confirmUnlessSkipped(flags, isInteractive, `Set up this directory as the workspace for "${project.displayName}"?`);
46
- if (!confirmed)
47
- return { success: false, error: 'Initialization cancelled.' };
48
- const stackName = slugify(project.displayName) || project.id;
49
- const studioName = await promptForStudioName(flags, isInteractive);
50
- log('Files to write:');
51
- log(' + sanity.blueprint.ts project + dataset (attached), studio, function');
52
- log(` + ${`studios/${studioName}/`.padEnd(32)}Studio with a "post" type`);
53
- log(` + ${`functions/${STARTER_FUNCTION_NAME}/`.padEnd(32)}sets a post slug from its title`);
54
- log(` + .sanity/blueprint.config.json org scope, stack ${stackName}`);
55
- log(' + .gitignore');
56
- log(' + package.json');
57
- log('');
58
- const scaffold = scaffoldWorkspace({
59
- dir,
60
- projectId: project.id,
61
- displayName: project.displayName,
62
- datasetName: dataset.name,
63
- stackName,
64
- studioName,
65
- blueprintsVersion: await getLatestNpmVersion('@sanity/blueprints', log),
66
- });
67
- return await createStackAndFinish({
68
- ...options,
69
- organizationId: organization.id,
70
- projectId: project.id,
71
- datasetName: dataset.name,
72
- stackName,
73
- blueprintFilePath: scaffold.blueprintFilePath,
74
- files: scaffold.files,
75
- nextSteps: [
76
- ...alignSteps([
77
- ['npm install'],
78
- [`npm run dev -w studios/${studioName}`, 'open the Studio and add a post'],
79
- [`npx ${bin} blueprints plan`, 'preview the deployment'],
80
- [`npx ${bin} blueprints deploy`, 'deploy the resources'],
81
- ]),
82
- '',
83
- 'Test the function on that post:',
84
- ` npx ${bin} functions test ${STARTER_FUNCTION_NAME} --document-id <id>` +
85
- ` --project-id ${project.id} --dataset ${dataset.name} --with-user-token`,
86
- ],
87
- });
88
- }
@@ -1,4 +0,0 @@
1
- import type { BlueprinitFlags } from './types.js';
2
- export declare const DEFAULT_STUDIO_NAME = "studio";
3
- export declare function confirmUnlessSkipped(flags: BlueprinitFlags, interactive: boolean, message: string): Promise<boolean>;
4
- export declare function promptForStudioName(flags: BlueprinitFlags, interactive: boolean): Promise<string>;
@@ -1,14 +0,0 @@
1
- import { confirm, input } from '@inquirer/prompts';
2
- import { slugify } from '../../actions/blueprinit/manifest.js';
3
- export const DEFAULT_STUDIO_NAME = 'studio';
4
- export async function confirmUnlessSkipped(flags, interactive, message) {
5
- if (flags.yes || !interactive)
6
- return true;
7
- return await confirm({ message, default: true });
8
- }
9
- export async function promptForStudioName(flags, interactive) {
10
- if (flags.yes || !interactive)
11
- return DEFAULT_STUDIO_NAME;
12
- const answer = await input({ message: 'Name your studio?', default: DEFAULT_STUDIO_NAME });
13
- return slugify(answer) || DEFAULT_STUDIO_NAME;
14
- }
@@ -1,15 +0,0 @@
1
- import type { Dataset } from '../../actions/sanity/datasets.js';
2
- import type { GroupedProjects, Project } from '../../actions/sanity/projects.js';
3
- import type { Result } from '../../utils/types.js';
4
- interface SelectionError {
5
- message: string;
6
- suggestions?: string[];
7
- }
8
- export declare function chooseOrganization(groups: GroupedProjects[], organizationId: string | undefined, interactive: boolean): Promise<Result<GroupedProjects, SelectionError>>;
9
- export declare function chooseProject(projects: Project[], projectId: string | undefined, organizationName: string, interactive: boolean): Promise<Result<Project, SelectionError>>;
10
- export declare function chooseDataset(datasets: Dataset[]): Dataset | undefined;
11
- export declare function findProject(groups: GroupedProjects[], projectId: string): {
12
- organization: GroupedProjects['organization'];
13
- project: Project;
14
- } | undefined;
15
- export {};
@@ -1,63 +0,0 @@
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 function chooseDataset(datasets) {
51
- return datasets.find(({ name }) => name === 'production') ?? datasets[0];
52
- }
53
- export function findProject(groups, projectId) {
54
- for (const { organization, projects = [] } of groups) {
55
- const project = projects.find(({ id }) => id === projectId);
56
- if (project)
57
- return { organization, project };
58
- }
59
- return undefined;
60
- }
61
- function failure(message, suggestions) {
62
- return { ok: false, error: { message, suggestions } };
63
- }
@@ -1,13 +0,0 @@
1
- import type { Logger } from '../../utils/logger.js';
2
- export interface BlueprinitFlags {
3
- yes?: boolean;
4
- organizationId?: string;
5
- projectId?: string;
6
- }
7
- export interface BlueprinitFlowOptions {
8
- bin: string;
9
- dir: string;
10
- flags: BlueprinitFlags;
11
- log: Logger;
12
- token: string;
13
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,7 +0,0 @@
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>;
@@ -1,39 +0,0 @@
1
- import { cwd } from 'node:process';
2
- import { detectStudio } from '../actions/blueprinit/studio.js';
3
- import { findBlueprintFile } from '../actions/blueprints/blueprint.js';
4
- import { validTokenOrErrorMessage } from '../utils/validated-token.js';
5
- import { adoptFlow } from './blueprinit/adopt.js';
6
- import { freshFlow } from './blueprinit/fresh.js';
7
- export async function blueprinitCore(options) {
8
- const { bin, log, flags = {}, dir = cwd() } = options;
9
- try {
10
- const existingBlueprint = findBlueprintFile(dir);
11
- if (existingBlueprint) {
12
- log(`A blueprint already exists here (${existingBlueprint.fileName}).`);
13
- log('');
14
- log('Use these instead:');
15
- log(` npx ${bin} blueprints config --edit`);
16
- log(` npx ${bin} blueprints doctor`);
17
- return {
18
- success: true,
19
- json: { alreadyInitialized: true, blueprintFile: existingBlueprint.fileName },
20
- };
21
- }
22
- const tokenCheck = await validTokenOrErrorMessage(log);
23
- if (!tokenCheck.ok) {
24
- return {
25
- success: false,
26
- error: 'No Sanity session found.',
27
- suggestions: ['Sign in first: npx sanity login', 'Then run blueprinit again.'],
28
- };
29
- }
30
- log('Setting up your Sanity workspace.');
31
- log('');
32
- const flowOptions = { bin, dir, flags, log, token: tokenCheck.value };
33
- const studio = detectStudio(dir);
34
- return studio ? await adoptFlow({ ...flowOptions, studio }) : await freshFlow(flowOptions);
35
- }
36
- catch (error) {
37
- return { success: false, error: error instanceof Error ? error.message : String(error) };
38
- }
39
- }