@sanity/runtime-cli 17.1.0 → 17.2.0-blueprinit.g62f8017
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 +86 -55
- package/SKILL.md +21 -0
- package/dist/actions/blueprinit/manifest.d.ts +13 -0
- package/dist/actions/blueprinit/manifest.js +57 -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 +34 -0
- package/dist/actions/blueprinit/templates.js +83 -0
- package/dist/actions/blueprinit/workspace.d.ts +15 -0
- package/dist/actions/blueprinit/workspace.js +142 -0
- package/dist/actions/blueprints/assets.d.ts +2 -1
- package/dist/actions/blueprints/assets.js +17 -26
- package/dist/actions/blueprints/blueprint.d.ts +1 -1
- package/dist/actions/blueprints/pre-deploy.d.ts +29 -0
- package/dist/actions/blueprints/pre-deploy.js +164 -0
- package/dist/actions/blueprints/wait-for-operation.js +4 -0
- package/dist/actions/sanity/datasets.d.ts +15 -0
- package/dist/actions/sanity/datasets.js +26 -0
- package/dist/actions/sanity/examples.d.ts +4 -4
- package/dist/baseCommands.d.ts +7 -4
- package/dist/baseCommands.js +17 -0
- package/dist/commands/blueprinit.d.ts +13 -0
- package/dist/commands/blueprinit.js +44 -0
- package/dist/commands/blueprints/add.d.ts +1 -1
- package/dist/commands/blueprints/config.d.ts +1 -1
- package/dist/commands/blueprints/deploy.d.ts +1 -1
- package/dist/commands/blueprints/destroy.d.ts +1 -1
- package/dist/commands/blueprints/doctor.d.ts +0 -1
- package/dist/commands/blueprints/doctor.js +3 -7
- package/dist/commands/blueprints/info.d.ts +1 -1
- package/dist/commands/blueprints/logs.d.ts +1 -1
- package/dist/commands/blueprints/mint-deploy-token.d.ts +1 -1
- package/dist/commands/blueprints/plan.d.ts +1 -1
- package/dist/commands/blueprints/promote.d.ts +1 -1
- package/dist/commands/blueprints/stacks.d.ts +1 -1
- package/dist/commands/functions/add.d.ts +1 -1
- package/dist/commands/functions/build.d.ts +1 -1
- package/dist/commands/functions/dev.d.ts +1 -1
- package/dist/commands/functions/env/add.d.ts +4 -1
- package/dist/commands/functions/env/add.js +6 -2
- package/dist/commands/functions/env/list.d.ts +4 -1
- package/dist/commands/functions/env/list.js +9 -3
- package/dist/commands/functions/env/remove.d.ts +4 -1
- package/dist/commands/functions/env/remove.js +9 -3
- package/dist/commands/functions/logs.d.ts +1 -1
- package/dist/commands/functions/test.d.ts +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/cores/blueprinit/adopt.d.ts +6 -0
- package/dist/cores/blueprinit/adopt.js +90 -0
- package/dist/cores/blueprinit/finish.d.ts +14 -0
- package/dist/cores/blueprinit/finish.js +36 -0
- package/dist/cores/blueprinit/fresh.d.ts +3 -0
- package/dist/cores/blueprinit/fresh.js +88 -0
- package/dist/cores/blueprinit/prompts.d.ts +4 -0
- package/dist/cores/blueprinit/prompts.js +14 -0
- package/dist/cores/blueprinit/selection.d.ts +15 -0
- package/dist/cores/blueprinit/selection.js +63 -0
- package/dist/cores/blueprinit/types.d.ts +13 -0
- package/dist/cores/blueprinit/types.js +1 -0
- package/dist/cores/blueprinit.d.ts +7 -0
- package/dist/cores/blueprinit.js +39 -0
- package/dist/cores/blueprints/deploy.js +18 -65
- package/dist/cores/blueprints/doctor.js +15 -12
- package/dist/cores/functions/build.js +3 -2
- package/dist/cores/functions/test.js +4 -2
- package/dist/server/app.js +8 -2
- package/dist/server/static/favicon-dark.svg +1 -0
- package/dist/server/static/favicon.svg +1 -0
- package/dist/server/static/index.html +1 -1
- package/dist/server/static/vendor/vendor.bundle.js +5 -0
- package/dist/utils/command-skills.d.ts +2 -0
- package/dist/utils/command-skills.js +17 -0
- package/dist/utils/functions/child-process-wrapper.js +13 -171
- package/dist/utils/functions/child-runtime/console.d.ts +1 -0
- package/dist/utils/functions/child-runtime/console.js +50 -0
- package/dist/utils/functions/child-runtime/invoke.d.ts +4 -0
- package/dist/utils/functions/child-runtime/invoke.js +47 -0
- package/dist/utils/functions/child-runtime/resources.d.ts +4 -0
- package/dist/utils/functions/child-runtime/resources.js +59 -0
- package/dist/utils/functions/find-entry-point.d.ts +1 -1
- package/dist/utils/functions/find-entry-point.js +10 -12
- package/dist/utils/functions/invoke/local.js +1 -1
- package/dist/utils/functions/invoke/prepare.d.ts +1 -1
- package/dist/utils/functions/invoke/prepare.js +19 -8
- package/dist/utils/functions/package-json.d.ts +1 -2
- package/dist/utils/functions/package-json.js +4 -6
- package/dist/utils/functions/prepare-asset.d.ts +2 -2
- package/dist/utils/functions/prepare-asset.js +18 -7
- package/dist/utils/functions/resolve-dependencies.d.ts +4 -1
- package/dist/utils/functions/resolve-dependencies.js +5 -6
- package/dist/utils/functions/resource-to-arc.d.ts +1 -1
- package/dist/utils/functions/resource-to-arc.js +4 -5
- package/dist/utils/functions/should-transpile.d.ts +7 -1
- package/dist/utils/functions/should-transpile.js +10 -10
- package/dist/utils/functions/transpile/function.d.ts +4 -1
- package/dist/utils/functions/transpile/function.js +9 -14
- package/dist/utils/functions/transpile/verify-handler.js +1 -1
- package/dist/utils/logger.d.ts +7 -5
- package/dist/utils/logger.js +33 -1
- package/dist/utils/progress.d.ts +5 -0
- package/dist/utils/progress.js +40 -0
- package/dist/utils/studios/prepare-asset.d.ts +6 -0
- package/dist/utils/studios/prepare-asset.js +139 -0
- package/dist/utils/types.d.ts +9 -8
- package/dist/utils/types.js +2 -3
- package/oclif.manifest.json +1218 -931
- package/package.json +33 -24
- package/skills/blueprinit.md +40 -0
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
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>;
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
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 {};
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
}
|
|
@@ -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,39 @@
|
|
|
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
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { preDeploy } from '../../actions/blueprints/pre-deploy.js';
|
|
3
3
|
import { getStack, updateStack } from '../../actions/blueprints/stacks.js';
|
|
4
4
|
import { waitForOperation } from '../../actions/blueprints/wait-for-operation.js';
|
|
5
5
|
import { checkUserPermission } from '../../actions/sanity/access.js';
|
|
@@ -7,19 +7,15 @@ import { createHintCollector } from '../../utils/blueprints/hints.js';
|
|
|
7
7
|
import { niceId } from '../../utils/display/presenters.js';
|
|
8
8
|
import { CODE_OPERATION_UNCONFIRMED, EXIT_OPERATION_UNCONFIRMED } from '../../utils/exit-codes.js';
|
|
9
9
|
import { styleText } from '../../utils/style-text.js';
|
|
10
|
-
import { isAssetResource } from '../../utils/types.js';
|
|
11
10
|
import { getWarnings } from '../../utils/warnings.js';
|
|
12
|
-
const DEFAULT_ASSET_TIMEOUT = 60;
|
|
13
|
-
const assetTimeoutS = Number(process.env.SANITY_ASSET_TIMEOUT) || DEFAULT_ASSET_TIMEOUT;
|
|
14
|
-
const assetTimeoutMs = assetTimeoutS * 1000;
|
|
15
|
-
const warnTimeoutMs = assetTimeoutMs / 2;
|
|
16
11
|
export async function blueprintDeployCore(options) {
|
|
17
12
|
const { bin = 'sanity', log, auth, stackId, scopeType, scopeId, deployedStack, blueprint, flags, } = options;
|
|
18
13
|
const { verbose } = flags;
|
|
19
14
|
const noWait = flags['no-wait'] || false;
|
|
20
15
|
const stackName = flags['new-stack-name'] ?? deployedStack.name;
|
|
21
16
|
const installer = flags['fn-installer'] || undefined;
|
|
22
|
-
const
|
|
17
|
+
const blueprintFileDir = dirname(blueprint.fileInfo.blueprintFilePath);
|
|
18
|
+
const workspaceWarnings = getWarnings({ dir: blueprintFileDir });
|
|
23
19
|
log(`Deploying "${stackName}" ${niceId(deployedStack.id)}...`);
|
|
24
20
|
try {
|
|
25
21
|
const { ok: checkOk, stack: currentStack } = await getStack({ stackId, auth, logger: log });
|
|
@@ -38,17 +34,21 @@ export async function blueprintDeployCore(options) {
|
|
|
38
34
|
};
|
|
39
35
|
}
|
|
40
36
|
const resources = [...blueprint.resources];
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
37
|
+
const preDeployResult = await preDeploy({
|
|
38
|
+
scopeType,
|
|
39
|
+
scopeId,
|
|
40
|
+
resources,
|
|
41
|
+
stackId,
|
|
42
|
+
stackName,
|
|
43
|
+
auth,
|
|
44
|
+
blueprintFileDir,
|
|
45
|
+
installer,
|
|
46
|
+
log,
|
|
47
|
+
bin,
|
|
48
|
+
verbose,
|
|
49
|
+
});
|
|
50
|
+
if (!preDeployResult?.success) {
|
|
51
|
+
return preDeployResult;
|
|
52
52
|
}
|
|
53
53
|
const spinner = log.ora('Deploying...').start();
|
|
54
54
|
const { ok: deployOk, stack, error: deployError, } = await updateStack({
|
|
@@ -151,53 +151,6 @@ export async function blueprintDeployCore(options) {
|
|
|
151
151
|
return { success: false, error: errorMessage };
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
async function preDeploy(resource, options) {
|
|
155
|
-
const { auth, installer, log } = options;
|
|
156
|
-
const fnSpinner = log.ora({ text: `Processing ${resource.name}...`, prefixText: ' ' }).start();
|
|
157
|
-
const warnTimer = setTimeout(() => {
|
|
158
|
-
fnSpinner.text = `Still processing ${resource.name}, this can take a moment...`;
|
|
159
|
-
}, warnTimeoutMs);
|
|
160
|
-
let assetTimeoutTimer;
|
|
161
|
-
let result;
|
|
162
|
-
try {
|
|
163
|
-
result = await Promise.race([
|
|
164
|
-
stashAsset({ resource, auth, logger: log, installer }),
|
|
165
|
-
new Promise((_, reject) => {
|
|
166
|
-
assetTimeoutTimer = setTimeout(() => {
|
|
167
|
-
reject(new Error(`Processing ${resource.name} timed out after ${assetTimeoutS}s`));
|
|
168
|
-
}, assetTimeoutMs);
|
|
169
|
-
}),
|
|
170
|
-
]);
|
|
171
|
-
}
|
|
172
|
-
catch (err) {
|
|
173
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
174
|
-
fnSpinner.fail(msg);
|
|
175
|
-
return { success: false, error: msg };
|
|
176
|
-
}
|
|
177
|
-
finally {
|
|
178
|
-
clearTimeout(warnTimer);
|
|
179
|
-
clearTimeout(assetTimeoutTimer);
|
|
180
|
-
}
|
|
181
|
-
if (result.success && result.assetId) {
|
|
182
|
-
const updatedResource = { ...resource, src: result.assetId };
|
|
183
|
-
fnSpinner.succeed(`${updatedResource.name} ${niceId(result.assetId)}`);
|
|
184
|
-
log(` Source: ${updatedResource.src}`);
|
|
185
|
-
if (result.hash) {
|
|
186
|
-
if (result.hash.length > 24) {
|
|
187
|
-
log.verbose(` Hash: ${result.hash.slice(0, 8)}...${result.hash.slice(-12)}`);
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
log.verbose(` Hash: ${result.hash}`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if (result.exists)
|
|
194
|
-
log.verbose(' Asset unchanged');
|
|
195
|
-
return { success: true, resource: updatedResource };
|
|
196
|
-
}
|
|
197
|
-
const errorMsg = `Failed uploading ${resource.name} asset, deploy has stopped`;
|
|
198
|
-
fnSpinner.fail(errorMsg);
|
|
199
|
-
return { success: false, error: result.error || 'Failed to process asset' };
|
|
200
|
-
}
|
|
201
154
|
const CHECK_LEGACY_DEPLOY_PERMISSION = false;
|
|
202
155
|
const BLUEPRINTS_PERMISSION_ENFORCEMENT_DATE = 'July 1, 2026';
|
|
203
156
|
async function checkLegacyDeployPermission({ auth, scopeType, scopeId, log, }) {
|
|
@@ -2,13 +2,13 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { arch, cwd, env, version as nodeVersion, platform } from 'node:process';
|
|
4
4
|
import * as resolve from 'empathic/resolve';
|
|
5
|
-
import ora from 'ora';
|
|
6
5
|
import { loadAndParseBlueprint, } from '../../actions/blueprints/blueprint.js';
|
|
7
6
|
import { readConfigFile } from '../../actions/blueprints/config.js';
|
|
8
7
|
import { resolveIds } from '../../actions/blueprints/resolve.js';
|
|
9
8
|
import { getStack } from '../../actions/blueprints/stacks.js';
|
|
10
9
|
import config, { RUNTIME_CLI_VERSION } from '../../config.js';
|
|
11
10
|
import { formatDateTime } from '../../utils/display/dates.js';
|
|
11
|
+
import { presentBlueprintParserErrors } from '../../utils/display/errors.js';
|
|
12
12
|
import { advise, check, filePathRelativeToCwd, niceId, renderSection, severe, unsure, } from '../../utils/display/presenters.js';
|
|
13
13
|
import { styleText } from '../../utils/style-text.js';
|
|
14
14
|
import { createTracedFetch } from '../../utils/traced-fetch.js';
|
|
@@ -55,7 +55,7 @@ export async function blueprintDoctorCore(options) {
|
|
|
55
55
|
envRows.push(['CLI', bin]);
|
|
56
56
|
const configRows = [];
|
|
57
57
|
const stackRows = [];
|
|
58
|
-
const spinner = ora('Running diagnostics...').start();
|
|
58
|
+
const spinner = log.ora('Running diagnostics...').start();
|
|
59
59
|
// --- ONLINE ---
|
|
60
60
|
const fetchFn = createTracedFetch(log);
|
|
61
61
|
try {
|
|
@@ -109,6 +109,7 @@ export async function blueprintDoctorCore(options) {
|
|
|
109
109
|
status: false,
|
|
110
110
|
detail: `${blueprint.errors.length} error(s)`,
|
|
111
111
|
};
|
|
112
|
+
log.verbose(presentBlueprintParserErrors(blueprint.errors));
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
catch {
|
|
@@ -186,6 +187,8 @@ export async function blueprintDoctorCore(options) {
|
|
|
186
187
|
stackRows.push(['Operation', `${op.status}${timestamp}`]);
|
|
187
188
|
if (op.userMessage)
|
|
188
189
|
stackRows.push([' Message', `"${op.userMessage}"`]);
|
|
190
|
+
if (v && op.systemMessage)
|
|
191
|
+
stackRows.push([' System', `"${op.systemMessage}"`]);
|
|
189
192
|
}
|
|
190
193
|
if (stack.resources) {
|
|
191
194
|
stackRows.push(['Resources', `${stack.resources.length}`]);
|
|
@@ -275,20 +278,20 @@ export async function blueprintDoctorCore(options) {
|
|
|
275
278
|
}
|
|
276
279
|
// --- RENDER REPORT ---
|
|
277
280
|
spinner.stop();
|
|
278
|
-
// Environment
|
|
279
|
-
log
|
|
280
|
-
log
|
|
281
|
-
// Configuration
|
|
281
|
+
// Environment
|
|
282
|
+
log(renderSection('Environment', envRows));
|
|
283
|
+
log('');
|
|
284
|
+
// Configuration
|
|
282
285
|
if (configRows.length > 0) {
|
|
283
|
-
log
|
|
284
|
-
log
|
|
286
|
+
log(renderSection('Configuration', configRows));
|
|
287
|
+
log('');
|
|
285
288
|
}
|
|
286
|
-
// Deployment
|
|
289
|
+
// Deployment
|
|
287
290
|
if (stackRows.length > 0) {
|
|
288
|
-
log
|
|
289
|
-
log
|
|
291
|
+
log(renderSection('Deployment', stackRows));
|
|
292
|
+
log('');
|
|
290
293
|
}
|
|
291
|
-
// Checks
|
|
294
|
+
// Checks
|
|
292
295
|
const maxLabel = Math.max(...Object.values(diagLookup).map((l) => l.length));
|
|
293
296
|
log(styleText('bold', 'Checks'));
|
|
294
297
|
let allGood = true;
|
|
@@ -18,13 +18,14 @@ export async function functionBuildCore(options) {
|
|
|
18
18
|
functions = allFunctions;
|
|
19
19
|
}
|
|
20
20
|
const installer = flags['fn-installer'] || undefined;
|
|
21
|
-
const
|
|
21
|
+
const blueprintFileDir = path.dirname(blueprint.fileInfo.blueprintFilePath);
|
|
22
|
+
const outDir = flags['out-dir'] || path.join(blueprintFileDir, '.build');
|
|
22
23
|
await fs.promises.mkdir(outDir, { recursive: true });
|
|
23
24
|
const errors = [];
|
|
24
25
|
const built = [];
|
|
25
26
|
for (const resource of functions) {
|
|
26
27
|
const spinner = log.ora(`Building ${resource.name}...`).start();
|
|
27
|
-
const prepResult = await prepareAsset({ resource
|
|
28
|
+
const prepResult = await prepareAsset({ resource, blueprintFileDir, installer });
|
|
28
29
|
if (prepResult.success === false) {
|
|
29
30
|
spinner.fail(`Failed to build ${resource.name}`);
|
|
30
31
|
errors.push({ name: resource.name, error: prepResult.error ?? 'Unknown error' });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
3
|
import { cwd } from 'node:process';
|
|
4
4
|
import { testAction } from '../../actions/functions/test.js';
|
|
5
5
|
import config from '../../config.js';
|
|
@@ -50,6 +50,7 @@ export async function functionTestCore(options) {
|
|
|
50
50
|
: undefined,
|
|
51
51
|
logger: log,
|
|
52
52
|
});
|
|
53
|
+
const blueprintFileDir = dirname(blueprint.fileInfo.blueprintFilePath);
|
|
53
54
|
const contextOptions = docFunction || mediaFunction
|
|
54
55
|
? {
|
|
55
56
|
clientOptions: {
|
|
@@ -63,8 +64,9 @@ export async function functionTestCore(options) {
|
|
|
63
64
|
functionResourceType: 'project',
|
|
64
65
|
functionResourceId: projectId || '',
|
|
65
66
|
resources: resolvedResources,
|
|
67
|
+
blueprintFileDir,
|
|
66
68
|
}
|
|
67
|
-
: { clientOptions: {}, resources: resolvedResources };
|
|
69
|
+
: { clientOptions: {}, resources: resolvedResources, blueprintFileDir };
|
|
68
70
|
// If the user sets the flag to use the real token set it in our options
|
|
69
71
|
if (withUserToken) {
|
|
70
72
|
contextOptions.clientOptions.token = config.token || undefined;
|
package/dist/server/app.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, watchFile } from 'node:fs';
|
|
2
2
|
import * as http from 'node:http';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
3
4
|
import { default as mime } from 'mime-types';
|
|
4
5
|
import { WebSocket, WebSocketServer } from 'ws';
|
|
5
6
|
import { findBlueprintFile, loadAndParseBlueprint } from '../actions/blueprints/blueprint.js';
|
|
@@ -68,7 +69,11 @@ const app = (host, port, validateResources, executionOptions) => {
|
|
|
68
69
|
req.on('end', async () => {
|
|
69
70
|
res.setHeader('Content-Type', 'application/json');
|
|
70
71
|
try {
|
|
71
|
-
const
|
|
72
|
+
const fileInfo = findBlueprintFile();
|
|
73
|
+
if (!fileInfo) {
|
|
74
|
+
throw Error('Could not find a Blueprint manifest (sanity.blueprint.ts, .js, or .json). Run `blueprints init` to create one.');
|
|
75
|
+
}
|
|
76
|
+
const { data, func: functionName, metadata, } = parseInvokeRequest(Buffer.concat(body), dirname(fileInfo.blueprintFilePath));
|
|
72
77
|
const { context, event } = data;
|
|
73
78
|
// replace user token if required
|
|
74
79
|
if (isGroqContextOptions(context)) {
|
|
@@ -312,7 +317,7 @@ function buildApiUrl(projectId, dataset, docId, apiUrl) {
|
|
|
312
317
|
function buildAssetUrl(mediaLibraryId, docId, apiUrl) {
|
|
313
318
|
return `${apiUrl}/v2025-03-24/media-libraries/${mediaLibraryId}/doc/${docId}`;
|
|
314
319
|
}
|
|
315
|
-
function parseInvokeRequest(body) {
|
|
320
|
+
function parseInvokeRequest(body, blueprintFileDir) {
|
|
316
321
|
let json;
|
|
317
322
|
try {
|
|
318
323
|
json = JSON.parse(body.toString('utf8'));
|
|
@@ -404,6 +409,7 @@ function parseInvokeRequest(body) {
|
|
|
404
409
|
context: {
|
|
405
410
|
...context,
|
|
406
411
|
clientOptions,
|
|
412
|
+
blueprintFileDir,
|
|
407
413
|
// Provide default values for required properties if not present
|
|
408
414
|
eventResourceType: typeof context.eventResourceType === 'string' ? context.eventResourceType : '',
|
|
409
415
|
eventResourceId: typeof context.eventResourceId === 'string' ? context.eventResourceId : '',
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
<filters-component></filters-component>
|
|
34
34
|
<input type="checkbox" id="nav-toggle" class="hidden">
|
|
35
35
|
<nav id="sidebar" class="relative y-scroll max-h-100 pad-t-5 border-right overflow-auto nav-min-width">
|
|
36
|
-
<label for="nav-toggle" id="nav-toggle-label" title="Toggle Functions list" class="z-10 nav-label"></label>
|
|
36
|
+
<label for="nav-toggle" id="nav-toggle-label" aria-label="Toggle Functions list" title="Toggle Functions list" class="z-10 nav-label"></label>
|
|
37
37
|
<h4 class="config-label mar-t-0 mar-b-3 pad-l-3">Functions</h4>
|
|
38
38
|
<function-list></function-list>
|
|
39
39
|
</nav>
|
|
@@ -6203,6 +6203,7 @@ class FacetProvider {
|
|
|
6203
6203
|
}
|
|
6204
6204
|
};
|
|
6205
6205
|
}
|
|
6206
|
+
get extension() { return this; }
|
|
6206
6207
|
}
|
|
6207
6208
|
function compareArray(a, b, compare) {
|
|
6208
6209
|
if (a.length != b.length)
|
|
@@ -6400,6 +6401,7 @@ class PrecExtension {
|
|
|
6400
6401
|
this.inner = inner;
|
|
6401
6402
|
this.prec = prec;
|
|
6402
6403
|
}
|
|
6404
|
+
get extension() { return this; }
|
|
6403
6405
|
}
|
|
6404
6406
|
/**
|
|
6405
6407
|
Extension compartments can be used to make a configuration
|
|
@@ -6434,6 +6436,7 @@ class CompartmentInstance {
|
|
|
6434
6436
|
this.compartment = compartment;
|
|
6435
6437
|
this.inner = inner;
|
|
6436
6438
|
}
|
|
6439
|
+
get extension() { return this; }
|
|
6437
6440
|
}
|
|
6438
6441
|
class Configuration {
|
|
6439
6442
|
constructor(base, compartments, dynamicSlots, address, staticValues, facets) {
|
|
@@ -6543,6 +6546,8 @@ function flatten(extension, compartments, newCompartments) {
|
|
|
6543
6546
|
else {
|
|
6544
6547
|
let content = ext.extension;
|
|
6545
6548
|
if (!content)
|
|
6549
|
+
throw new Error(`Unrecognized extension value in extension set (${ext}).`);
|
|
6550
|
+
if (content == ext)
|
|
6546
6551
|
throw new Error(`Unrecognized extension value in extension set (${ext}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);
|
|
6547
6552
|
inner(content, prec);
|
|
6548
6553
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
const SKILLS_DIRECTORY = fileURLToPath(new URL('../../skills/', import.meta.url));
|
|
5
|
+
export function commandSkillFilename(commandId) {
|
|
6
|
+
return `${commandId.replaceAll(':', '-')}.md`;
|
|
7
|
+
}
|
|
8
|
+
export async function readCommandSkill(commandId, skillsDirectory = SKILLS_DIRECTORY) {
|
|
9
|
+
try {
|
|
10
|
+
return await readFile(join(skillsDirectory, commandSkillFilename(commandId)), 'utf8');
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error.code === 'ENOENT')
|
|
14
|
+
return;
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|