@sanity/runtime-cli 12.0.1 → 12.2.0
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 +25 -24
- package/dist/actions/blueprints/blueprint.d.ts +5 -4
- package/dist/actions/blueprints/config.d.ts +7 -6
- package/dist/actions/blueprints/config.js +8 -6
- package/dist/actions/blueprints/stacks.d.ts +8 -4
- package/dist/actions/blueprints/stacks.js +6 -32
- package/dist/commands/blueprints/destroy.js +2 -0
- package/dist/commands/blueprints/doctor.d.ts +1 -0
- package/dist/commands/blueprints/doctor.js +4 -0
- package/dist/commands/blueprints/info.js +1 -1
- package/dist/commands/blueprints/init.js +1 -1
- package/dist/commands/blueprints/stacks.js +1 -1
- package/dist/commands/functions/dev.d.ts +2 -2
- package/dist/commands/functions/dev.js +3 -2
- package/dist/config.js +12 -2
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +7 -0
- package/dist/cores/blueprints/config.js +36 -39
- package/dist/cores/blueprints/doctor.d.ts +1 -0
- package/dist/cores/blueprints/doctor.js +36 -5
- package/dist/cores/blueprints/info.js +2 -2
- package/dist/cores/blueprints/init.d.ts +2 -0
- package/dist/cores/blueprints/init.js +38 -14
- package/dist/cores/functions/env/add.js +2 -2
- package/dist/cores/functions/env/list.js +2 -2
- package/dist/cores/functions/env/remove.js +2 -2
- package/dist/cores/functions/logs.js +2 -2
- package/dist/cores/functions/test.js +31 -23
- package/dist/server/app.js +32 -21
- package/dist/server/handlers/invoke.d.ts +2 -2
- package/dist/server/handlers/invoke.js +2 -2
- package/dist/server/static/components/api-base.js +3 -0
- package/dist/server/static/components/app.css +120 -95
- package/dist/server/static/components/clear-button.js +1 -1
- package/dist/server/static/components/console-panel.js +6 -6
- package/dist/server/static/components/fetch-button.js +1 -1
- package/dist/server/static/components/filter-api-version.js +3 -3
- package/dist/server/static/components/filter-document-id.js +5 -5
- package/dist/server/static/components/filter-with-token.js +4 -4
- package/dist/server/static/components/filters.js +2 -2
- package/dist/server/static/components/function-list.js +14 -5
- package/dist/server/static/components/help-button.js +4 -1
- package/dist/server/static/components/payload-panel.js +9 -9
- package/dist/server/static/components/response-panel.js +8 -8
- package/dist/server/static/components/rule-panel.js +4 -4
- package/dist/server/static/components/run-panel.js +4 -4
- package/dist/server/static/components/select-dropdown.js +5 -25
- package/dist/server/static/index.html +9 -9
- package/dist/server/static/vendor/m-.css +1 -0
- package/dist/server/static/vendor/m-.woff2 +0 -0
- package/dist/utils/display/blueprints-formatting.d.ts +3 -2
- package/dist/utils/display/blueprints-formatting.js +102 -50
- package/dist/utils/display/presenters.d.ts +1 -0
- package/dist/utils/display/presenters.js +3 -0
- package/dist/utils/display/prompt.js +10 -9
- package/dist/utils/display/resources-formatting.d.ts +6 -2
- package/dist/utils/display/resources-formatting.js +71 -17
- package/dist/utils/find-function.d.ts +2 -2
- package/dist/utils/find-function.js +9 -2
- package/dist/utils/invoke-local.d.ts +2 -2
- package/dist/utils/invoke-local.js +27 -16
- package/dist/utils/types.d.ts +46 -22
- package/dist/utils/types.js +25 -2
- package/dist/utils/validate/resource.js +144 -23
- package/dist/utils/validated-token.js +1 -1
- package/oclif.manifest.json +23 -4
- package/package.json +7 -4
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { highlight } from 'cardinal';
|
|
2
1
|
import chalk from 'chalk';
|
|
3
2
|
import { patchConfigFile, writeConfigFile, } from '../../actions/blueprints/config.js';
|
|
4
|
-
import {
|
|
5
|
-
import { capitalize, filePathRelativeToCwd, niceId, warn } from '../../utils/display/presenters.js';
|
|
3
|
+
import { filePathRelativeToCwd, labeledId, warn } from '../../utils/display/presenters.js';
|
|
6
4
|
import { promptForProject, promptForStack } from '../../utils/display/prompt.js';
|
|
7
5
|
export async function blueprintConfigCore(options) {
|
|
8
6
|
const { bin = 'sanity', blueprint, log, token, flags } = options;
|
|
9
7
|
const { edit: editConfig = false, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, 'stack-id': flagStackId, verbose: v = false, } = flags;
|
|
8
|
+
const providedConfigFlag = [flagProjectId, flagStackId, flagOrganizationId].some(Boolean);
|
|
10
9
|
const { stackId: configStackId, scopeType: configScopeType, scopeId: configScopeId, blueprintConfig, fileInfo, } = blueprint;
|
|
11
10
|
const blueprintFilePath = fileInfo.blueprintFilePath;
|
|
12
|
-
const configPath = blueprintConfig?.configPath;
|
|
13
11
|
if (!configStackId && !configScopeType && !configScopeId) {
|
|
14
12
|
log(warn('Incomplete configuration.'));
|
|
15
13
|
if (!editConfig) {
|
|
@@ -18,19 +16,10 @@ export async function blueprintConfigCore(options) {
|
|
|
18
16
|
return { success: true }; // not necessarily fatal
|
|
19
17
|
}
|
|
20
18
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (v)
|
|
24
|
-
log(` File: ${filePathRelativeToCwd(configPath)}`);
|
|
25
|
-
}
|
|
26
|
-
log(` Deployment: ${chalk.blue('Stack')} ${niceId(configStackId || 'unknown')}`);
|
|
27
|
-
log(` Scoped to: ${chalk.blue(capitalize(configScopeType || 'unknown'))} ${niceId(configScopeId || 'unknown')}`);
|
|
28
|
-
if (blueprintConfig?.updatedAt) {
|
|
29
|
-
if (v)
|
|
30
|
-
log(` Updated: ${new Date(blueprintConfig.updatedAt).toLocaleString()}`);
|
|
31
|
-
}
|
|
19
|
+
if (blueprintConfig)
|
|
20
|
+
printConfig({ configLabel: 'Current', log, config: blueprintConfig, v });
|
|
32
21
|
// passing new config without --edit flag is not allowed
|
|
33
|
-
if (
|
|
22
|
+
if (providedConfigFlag && !editConfig) {
|
|
34
23
|
log('To update the configuration, use the --edit flag.');
|
|
35
24
|
return { success: true };
|
|
36
25
|
}
|
|
@@ -39,10 +28,9 @@ export async function blueprintConfigCore(options) {
|
|
|
39
28
|
return { success: true };
|
|
40
29
|
}
|
|
41
30
|
else {
|
|
42
|
-
// if a config property flag was passed, set the value and return success
|
|
43
|
-
// do not try to validate correctness of combined flags; this should not be interactive
|
|
44
|
-
const providedConfigFlag = [flagProjectId, flagStackId, flagOrganizationId].some(Boolean);
|
|
45
31
|
if (providedConfigFlag) {
|
|
32
|
+
// if a config property flag was passed, set the value and return success
|
|
33
|
+
// do not try to validate correctness of combined flags; this should not be interactive
|
|
46
34
|
const configUpdate = {};
|
|
47
35
|
if (flagProjectId)
|
|
48
36
|
configUpdate.projectId = flagProjectId;
|
|
@@ -51,19 +39,16 @@ export async function blueprintConfigCore(options) {
|
|
|
51
39
|
if (flagOrganizationId)
|
|
52
40
|
configUpdate.organizationId = flagOrganizationId;
|
|
53
41
|
try {
|
|
54
|
-
patchConfigFile(blueprintFilePath, configUpdate);
|
|
42
|
+
const newConfig = patchConfigFile(blueprintFilePath, configUpdate);
|
|
43
|
+
printConfig({ configLabel: 'Updated', log, config: newConfig, v });
|
|
44
|
+
return { success: true };
|
|
55
45
|
}
|
|
56
46
|
catch {
|
|
57
47
|
return { success: false, error: 'Unable to update configuration.' };
|
|
58
48
|
}
|
|
59
|
-
log('\nConfiguration updated.');
|
|
60
|
-
return { success: true };
|
|
61
|
-
}
|
|
62
|
-
// organization-based Blueprints are not yet supported for interactive editing
|
|
63
|
-
if (configScopeType === 'organization') {
|
|
64
|
-
return { success: false, error: 'Only project-based Blueprints are supported.' };
|
|
65
49
|
}
|
|
66
|
-
//
|
|
50
|
+
// prompt for values interactively
|
|
51
|
+
// do not yet offer organization as scope option
|
|
67
52
|
let updatedProjectId = flagProjectId;
|
|
68
53
|
if (!updatedProjectId) {
|
|
69
54
|
const pickedProject = await promptForProject({
|
|
@@ -83,21 +68,33 @@ export async function blueprintConfigCore(options) {
|
|
|
83
68
|
return { success: false, error: 'Stack is required.' };
|
|
84
69
|
try {
|
|
85
70
|
// update or create config JSON
|
|
86
|
-
writeConfigFile(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
log('Configuration updated.');
|
|
71
|
+
const newConfig = await writeConfigFile(blueprintFilePath, {
|
|
72
|
+
projectId: updatedProjectId,
|
|
73
|
+
stackId: updatedStackId,
|
|
74
|
+
});
|
|
75
|
+
printConfig({ configLabel: 'Updated', log, config: newConfig, v });
|
|
92
76
|
return { success: true };
|
|
93
77
|
}
|
|
94
78
|
catch {
|
|
95
|
-
|
|
96
|
-
log(highlight(JSON.stringify({ metadata: { projectId: updatedProjectId, stackId: updatedStackId } }, null, 2)));
|
|
97
|
-
return {
|
|
98
|
-
success: false,
|
|
99
|
-
error: `Be sure to update your ${BLUEPRINT_CONFIG_DIR}/${BLUEPRINT_CONFIG_FILE}`,
|
|
100
|
-
};
|
|
79
|
+
return { success: false, error: 'Unable to update configuration!' };
|
|
101
80
|
}
|
|
102
81
|
}
|
|
103
82
|
}
|
|
83
|
+
function printConfig(options) {
|
|
84
|
+
const { configLabel, log, config, v = false } = options;
|
|
85
|
+
const { projectId, organizationId, stackId, updatedAt } = config;
|
|
86
|
+
const scopeType = projectId ? 'project' : 'organization';
|
|
87
|
+
const scopeId = projectId ? projectId : organizationId;
|
|
88
|
+
if (v)
|
|
89
|
+
log(JSON.stringify(config));
|
|
90
|
+
log(`${chalk.bold(`${configLabel} configuration:`)}`);
|
|
91
|
+
log(` Deployment: ${labeledId('stack', stackId)}`);
|
|
92
|
+
log(` Scoped to: ${labeledId(scopeType, scopeId)}`);
|
|
93
|
+
if (updatedAt)
|
|
94
|
+
log(` Updated: ${new Date(updatedAt).toLocaleString()}`);
|
|
95
|
+
if ('configPath' in config) {
|
|
96
|
+
const { configPath } = config;
|
|
97
|
+
if (configPath)
|
|
98
|
+
log(` File: ${filePathRelativeToCwd(configPath)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -5,6 +5,7 @@ import { getStack } from '../../actions/blueprints/stacks.js';
|
|
|
5
5
|
import config from '../../config.js';
|
|
6
6
|
import { capitalize, check, filePathRelativeToCwd, indent, niceId, severe, unsure, } from '../../utils/display/presenters.js';
|
|
7
7
|
import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
|
|
8
|
+
import { blueprintConfigCore } from './config.js';
|
|
8
9
|
const diagLookup = {
|
|
9
10
|
online: 'Online',
|
|
10
11
|
tokenPresent: 'Auth token present',
|
|
@@ -17,12 +18,13 @@ const diagLookup = {
|
|
|
17
18
|
userHasAccess: 'User has access to deployed "Stack"',
|
|
18
19
|
};
|
|
19
20
|
export async function blueprintDoctorCore(options) {
|
|
20
|
-
const { log, token, flags: { verbose: v, path: p }, } = options;
|
|
21
|
+
const { bin, log, token, flags: { verbose: v, path: p, fix }, } = options;
|
|
21
22
|
const yikes = (s) => {
|
|
22
23
|
log(chalk.bgRedBright.whiteBright.bold(` ${s} `));
|
|
23
24
|
};
|
|
24
25
|
const here = cwd();
|
|
25
26
|
const path = p || here;
|
|
27
|
+
let tokenOrError;
|
|
26
28
|
if (v)
|
|
27
29
|
log(`Checking ${filePathRelativeToCwd(path)}`);
|
|
28
30
|
// 3 states: null == unknown, true == good, false == bad
|
|
@@ -49,7 +51,7 @@ export async function blueprintDoctorCore(options) {
|
|
|
49
51
|
// TOKEN
|
|
50
52
|
if (token) {
|
|
51
53
|
diagnostics.tokenPresent = true;
|
|
52
|
-
|
|
54
|
+
tokenOrError = await validTokenOrErrorMessage(token);
|
|
53
55
|
if (tokenOrError.ok) {
|
|
54
56
|
diagnostics.tokenValid = true;
|
|
55
57
|
}
|
|
@@ -91,7 +93,8 @@ export async function blueprintDoctorCore(options) {
|
|
|
91
93
|
diagnostics.blueprintValid = false;
|
|
92
94
|
}
|
|
93
95
|
if (blueprint) {
|
|
94
|
-
const {
|
|
96
|
+
const { blueprintConfig, scopeType, scopeId, stackId, projectId } = blueprint;
|
|
97
|
+
const configPath = blueprintConfig?.configPath;
|
|
95
98
|
// CONFIG file
|
|
96
99
|
if (configPath) {
|
|
97
100
|
if (v)
|
|
@@ -169,11 +172,39 @@ export async function blueprintDoctorCore(options) {
|
|
|
169
172
|
log(severe(`${key} is ${value}`));
|
|
170
173
|
}
|
|
171
174
|
}
|
|
175
|
+
const errorMessage = 'One or more checks failed';
|
|
172
176
|
if (allGood) {
|
|
173
177
|
log(chalk.bold.green('All checks passed'));
|
|
178
|
+
if (fix)
|
|
179
|
+
log(chalk.bold.yellow('Nothing to fix; --fix flag is ignored'));
|
|
174
180
|
return { success: true, data: { diagnostics } };
|
|
175
181
|
}
|
|
176
|
-
else {
|
|
177
|
-
|
|
182
|
+
else if (fix) {
|
|
183
|
+
if (p) {
|
|
184
|
+
return { success: false, error: `${errorMessage}. --fix cannot be used with --path` };
|
|
185
|
+
}
|
|
186
|
+
if (!tokenOrError) {
|
|
187
|
+
return { success: false, error: `${errorMessage}. Unable to fix: Missing authentication token` };
|
|
188
|
+
}
|
|
189
|
+
if (tokenOrError?.ok === false) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
error: `${errorMessage}. Unable to fix: ${tokenOrError.error.message}`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (!blueprint) {
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
error: `${errorMessage}. Unable to fix: Blueprint is missing or invalid`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return blueprintConfigCore({
|
|
202
|
+
bin,
|
|
203
|
+
log,
|
|
204
|
+
token: tokenOrError.value,
|
|
205
|
+
blueprint,
|
|
206
|
+
flags: { edit: true, verbose: v },
|
|
207
|
+
});
|
|
178
208
|
}
|
|
209
|
+
return { success: false, error: errorMessage, data: { diagnostics } };
|
|
179
210
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getStack } from '../../actions/blueprints/stacks.js';
|
|
2
|
-
import {
|
|
2
|
+
import { formatDeployedResourceTree, formatStackInfo, } from '../../utils/display/blueprints-formatting.js';
|
|
3
3
|
import { niceId } from '../../utils/display/presenters.js';
|
|
4
4
|
export async function blueprintInfoCore(options) {
|
|
5
5
|
const { log, auth, stackId, flags, deployedStack } = options;
|
|
@@ -20,7 +20,7 @@ export async function blueprintInfoCore(options) {
|
|
|
20
20
|
}
|
|
21
21
|
log(formatStackInfo(stack, true));
|
|
22
22
|
if (stack.resources)
|
|
23
|
-
log(
|
|
23
|
+
log(formatDeployedResourceTree(stack.resources));
|
|
24
24
|
return { success: true };
|
|
25
25
|
}
|
|
26
26
|
catch (error) {
|
|
@@ -33,11 +33,13 @@ export declare function resolveScopeAndStack(params: {
|
|
|
33
33
|
organizationId: string | undefined;
|
|
34
34
|
stackId: string | undefined;
|
|
35
35
|
stackName: string | undefined;
|
|
36
|
+
log: (message: string) => void;
|
|
36
37
|
token: string;
|
|
37
38
|
}): Promise<ResolvedScope>;
|
|
38
39
|
export declare function determineBlueprintExtension(params: {
|
|
39
40
|
requestedType: string | undefined;
|
|
40
41
|
blueprintDir: string;
|
|
42
|
+
log: (message: string) => void;
|
|
41
43
|
}): Promise<string>;
|
|
42
44
|
export declare function createBlueprintFiles(params: {
|
|
43
45
|
blueprintDir: string;
|
|
@@ -9,7 +9,7 @@ import { writeGitignoreFile } from '../../actions/git.js';
|
|
|
9
9
|
import { writeOrUpdateNodeDependency } from '../../actions/node.js';
|
|
10
10
|
import { verifyExampleExists, writeExample } from '../../actions/sanity/examples.js';
|
|
11
11
|
import { BLUEPRINT_CONFIG_DIR, BLUEPRINT_CONFIG_FILE } from '../../config.js';
|
|
12
|
-
import { check, filePathRelativeToCwd, warn } from '../../utils/display/presenters.js';
|
|
12
|
+
import { check, filePathRelativeToCwd, labeledId, warn } from '../../utils/display/presenters.js';
|
|
13
13
|
import { promptForBlueprintType, promptForProject, promptForStack, } from '../../utils/display/prompt.js';
|
|
14
14
|
import { blueprintConfigCore } from './config.js';
|
|
15
15
|
const SCOPE_PROJECT = 'project';
|
|
@@ -20,6 +20,7 @@ export async function blueprintInitCore(options) {
|
|
|
20
20
|
const { dir: argDir } = args;
|
|
21
21
|
const userProvidedDirName = argDir || flagDir;
|
|
22
22
|
const blueprintDir = userProvidedDirName || '.';
|
|
23
|
+
const flagProvidedConfig = [flagProjectId, flagOrganizationId, flagStackId].some(Boolean);
|
|
23
24
|
const validationError = validateFlags({
|
|
24
25
|
stackId: flagStackId,
|
|
25
26
|
stackName: flagStackName,
|
|
@@ -29,15 +30,30 @@ export async function blueprintInitCore(options) {
|
|
|
29
30
|
if (validationError)
|
|
30
31
|
return validationError;
|
|
31
32
|
try {
|
|
33
|
+
// look for existing blueprint file and maybe re-configure it
|
|
32
34
|
const existingBlueprintFile = findBlueprintFile(blueprintDir);
|
|
33
35
|
if (existingBlueprintFile) {
|
|
34
36
|
log(warn(`Existing Blueprint found: ${filePathRelativeToCwd(existingBlueprintFile.blueprintFilePath)}`));
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
if (flagExample) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: 'Flag --example cannot be used with an existing Blueprint.',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (flagStackName) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
error: 'Flag --stack-name cannot be used with an existing Blueprint.',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (!flagProvidedConfig) {
|
|
50
|
+
const overwrite = await confirm({
|
|
51
|
+
message: 'Would you like to re-configure deployment for this Blueprint?',
|
|
52
|
+
default: true,
|
|
53
|
+
});
|
|
54
|
+
if (!overwrite)
|
|
55
|
+
return { success: false, error: 'Initialization cancelled.' };
|
|
56
|
+
}
|
|
41
57
|
const existingBlueprint = await readLocalBlueprint(existingBlueprintFile.blueprintFilePath);
|
|
42
58
|
return blueprintConfigCore({
|
|
43
59
|
blueprint: existingBlueprint,
|
|
@@ -68,11 +84,13 @@ export async function blueprintInitCore(options) {
|
|
|
68
84
|
organizationId: flagOrganizationId,
|
|
69
85
|
stackId: flagStackId,
|
|
70
86
|
stackName: flagStackName,
|
|
87
|
+
log,
|
|
71
88
|
token,
|
|
72
89
|
});
|
|
73
90
|
const blueprintExtension = await determineBlueprintExtension({
|
|
74
91
|
requestedType: flagBlueprintType,
|
|
75
92
|
blueprintDir,
|
|
93
|
+
log,
|
|
76
94
|
});
|
|
77
95
|
return createBlueprintFiles({
|
|
78
96
|
blueprintDir,
|
|
@@ -87,7 +105,6 @@ export async function blueprintInitCore(options) {
|
|
|
87
105
|
}
|
|
88
106
|
catch (error) {
|
|
89
107
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
90
|
-
log(`Error: ${errorMessage}`);
|
|
91
108
|
return { success: false, error: errorMessage };
|
|
92
109
|
}
|
|
93
110
|
}
|
|
@@ -136,7 +153,7 @@ async function handleExampleInitialization(options) {
|
|
|
136
153
|
return { success: false, error: 'Failed to find blueprint file.' };
|
|
137
154
|
}
|
|
138
155
|
const { blueprintFilePath } = discoveredBlueprint;
|
|
139
|
-
writeConfigFile(
|
|
156
|
+
writeConfigFile(blueprintFilePath, { projectId: resolvedProjectId, stackId: stack.id });
|
|
140
157
|
log(check(`${chalk.bold('Configured:')} ${exampleDir}/${BLUEPRINT_CONFIG_DIR}/${BLUEPRINT_CONFIG_FILE}`));
|
|
141
158
|
log(`\n Run "${chalk.bold.magenta(`cd ${exampleDir} && npm i`)}" and check out the README`);
|
|
142
159
|
if (instructions) {
|
|
@@ -146,7 +163,7 @@ async function handleExampleInitialization(options) {
|
|
|
146
163
|
return { success: true };
|
|
147
164
|
}
|
|
148
165
|
export async function resolveScopeAndStack(params) {
|
|
149
|
-
const { projectId, organizationId, stackId, stackName, token } = params;
|
|
166
|
+
const { projectId, organizationId, stackId, stackName, log, token } = params;
|
|
150
167
|
let scopeType = SCOPE_PROJECT;
|
|
151
168
|
let scopeId;
|
|
152
169
|
if (projectId) {
|
|
@@ -159,7 +176,9 @@ export async function resolveScopeAndStack(params) {
|
|
|
159
176
|
}
|
|
160
177
|
let resolvedStackId = stackId;
|
|
161
178
|
if (!resolvedStackId && stackName && scopeType && scopeId) {
|
|
179
|
+
// sending stackName will assume you want to create a new stack
|
|
162
180
|
// essentially the only way to create an org-scoped stack
|
|
181
|
+
log(`\nCreating new Stack "${stackName}" scoped to ${labeledId(scopeType, scopeId)}`);
|
|
163
182
|
const stack = await createEmptyStack({
|
|
164
183
|
token,
|
|
165
184
|
scopeType,
|
|
@@ -169,11 +188,13 @@ export async function resolveScopeAndStack(params) {
|
|
|
169
188
|
resolvedStackId = stack.id;
|
|
170
189
|
}
|
|
171
190
|
if (!scopeId) {
|
|
191
|
+
log('\nBlueprints are associated with a Sanity Project. Please select one:');
|
|
172
192
|
const pickedProject = await promptForProject({ token });
|
|
173
193
|
scopeType = SCOPE_PROJECT;
|
|
174
194
|
scopeId = pickedProject.projectId;
|
|
175
195
|
}
|
|
176
196
|
if (!resolvedStackId) {
|
|
197
|
+
log('\nBlueprints are deployed to a "Stack".');
|
|
177
198
|
const { stackId } = await promptForStack({ projectId: scopeId, token });
|
|
178
199
|
resolvedStackId = stackId;
|
|
179
200
|
}
|
|
@@ -184,11 +205,14 @@ export async function resolveScopeAndStack(params) {
|
|
|
184
205
|
};
|
|
185
206
|
}
|
|
186
207
|
export async function determineBlueprintExtension(params) {
|
|
187
|
-
const { requestedType, blueprintDir } = params;
|
|
208
|
+
const { requestedType, blueprintDir, log } = params;
|
|
188
209
|
let extension = requestedType;
|
|
189
|
-
if (!extension)
|
|
210
|
+
if (!extension) {
|
|
211
|
+
log('\nBlueprint files are authored locally to create resources in your Stack.');
|
|
190
212
|
extension = await promptForBlueprintType();
|
|
213
|
+
}
|
|
191
214
|
if (extension === 'js') {
|
|
215
|
+
// do we need to use .mjs or .js?
|
|
192
216
|
const packageJsonPath = join(blueprintDir, 'package.json');
|
|
193
217
|
const packageExists = existsSync(packageJsonPath);
|
|
194
218
|
if (packageExists) {
|
|
@@ -209,6 +233,7 @@ export async function createBlueprintFiles(params) {
|
|
|
209
233
|
if (!blueprintExtension) {
|
|
210
234
|
return { success: false, error: 'Blueprint type is required.' };
|
|
211
235
|
}
|
|
236
|
+
log(`\nCreating local Blueprint setup...`);
|
|
212
237
|
const blueprintFileName = `sanity.blueprint.${blueprintExtension}`;
|
|
213
238
|
const blueprintFilePath = join(blueprintDir, blueprintFileName);
|
|
214
239
|
writeBlueprintToDisk({ blueprintFilePath });
|
|
@@ -217,8 +242,7 @@ export async function createBlueprintFiles(params) {
|
|
|
217
242
|
}
|
|
218
243
|
const displayPath = userProvidedDirName || '.';
|
|
219
244
|
log(check(`${chalk.bold('Created Blueprint:')} ${displayPath}/${blueprintFileName}`));
|
|
220
|
-
writeConfigFile({
|
|
221
|
-
blueprintFilePath,
|
|
245
|
+
writeConfigFile(blueprintFilePath, {
|
|
222
246
|
stackId,
|
|
223
247
|
...(scopeType === SCOPE_ORGANIZATION ? { organizationId: scopeId } : { projectId: scopeId }),
|
|
224
248
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { update } from '../../../actions/functions/env/update.js';
|
|
4
|
-
import {
|
|
4
|
+
import { findFunctionInStack } from '../../../utils/find-function.js';
|
|
5
5
|
export async function functionEnvAddCore(options) {
|
|
6
6
|
const args = options.args;
|
|
7
7
|
const spinner = ora(`Updating "${args.key}" environment variable in "${args.name}"`).start();
|
|
8
|
-
const { externalId } =
|
|
8
|
+
const { externalId } = findFunctionInStack(options.deployedStack, args.name);
|
|
9
9
|
const result = await update(externalId, args.key, args.value, options.auth);
|
|
10
10
|
if (!result.ok) {
|
|
11
11
|
spinner.fail(`${chalk.red('Failed')} to update ${args.key}`);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import ora from 'ora';
|
|
2
2
|
import { list } from '../../../actions/functions/env/list.js';
|
|
3
|
-
import {
|
|
3
|
+
import { findFunctionInStack } from '../../../utils/find-function.js';
|
|
4
4
|
export async function functionEnvListCore(options) {
|
|
5
5
|
const args = options.args;
|
|
6
6
|
const spinner = ora(`Listing environment variables for "${args.name}"`).start();
|
|
7
|
-
const { externalId } =
|
|
7
|
+
const { externalId } = findFunctionInStack(options.deployedStack, args.name);
|
|
8
8
|
const result = await list(externalId, options.auth);
|
|
9
9
|
if (!result.ok) {
|
|
10
10
|
spinner.stop();
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { remove } from '../../../actions/functions/env/remove.js';
|
|
4
|
-
import {
|
|
4
|
+
import { findFunctionInStack } from '../../../utils/find-function.js';
|
|
5
5
|
export async function functionEnvRemoveCore(options) {
|
|
6
6
|
const args = options.args;
|
|
7
7
|
const spinner = ora(`Removing "${args.key}" environment variable in "${args.name}"`).start();
|
|
8
|
-
const { externalId } =
|
|
8
|
+
const { externalId } = findFunctionInStack(options.deployedStack, args.name);
|
|
9
9
|
const result = await remove(externalId, args.key, options.auth);
|
|
10
10
|
if (!result.ok) {
|
|
11
11
|
spinner.fail(`${chalk.red('Failed')} to remove ${args.key}`);
|
|
@@ -4,12 +4,12 @@ import ora from 'ora';
|
|
|
4
4
|
import { deleteLogs as deleteLogsAction, logs as getLogsAction, streamLogs as streamLogsAction, } from '../../actions/functions/logs.js';
|
|
5
5
|
import { formatTitle } from '../../utils/display/blueprints-formatting.js';
|
|
6
6
|
import { niceId } from '../../utils/display/presenters.js';
|
|
7
|
-
import {
|
|
7
|
+
import { findFunctionInStack } from '../../utils/find-function.js';
|
|
8
8
|
export async function functionLogsCore(options) {
|
|
9
9
|
const { args, flags, log, auth, deployedStack } = options;
|
|
10
10
|
const { name } = args;
|
|
11
11
|
const { delete: shouldDelete, watch: shouldWatch, force, limit, json, utc } = flags;
|
|
12
|
-
const { externalId } =
|
|
12
|
+
const { externalId } = findFunctionInStack(deployedStack, name); // throws if not found
|
|
13
13
|
if (shouldDelete)
|
|
14
14
|
return deleteLogs({ name, externalId, auth, force, log });
|
|
15
15
|
if (shouldWatch)
|
|
@@ -4,11 +4,12 @@ import { cwd } from 'node:process';
|
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import { testAction } from '../../actions/functions/test.js';
|
|
6
6
|
import config from '../../config.js';
|
|
7
|
+
import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET } from '../../constants.js';
|
|
7
8
|
import buildPayload from '../../utils/build-payload.js';
|
|
8
|
-
import {
|
|
9
|
+
import { findFunctionInBlueprint } from '../../utils/find-function.js';
|
|
9
10
|
import { fetchAsset, fetchDocument } from '../../utils/functions/fetch-document.js';
|
|
10
11
|
import { parseJsonObject } from '../../utils/parse-json-object.js';
|
|
11
|
-
import { isEventType, } from '../../utils/types.js';
|
|
12
|
+
import { isEventType, isGroqContextOptions, } from '../../utils/types.js';
|
|
12
13
|
export async function functionTestCore(options) {
|
|
13
14
|
const { blueprint, log, args, flags } = options;
|
|
14
15
|
const { name: fnName } = args;
|
|
@@ -29,22 +30,25 @@ export async function functionTestCore(options) {
|
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
try {
|
|
32
|
-
const resource =
|
|
33
|
-
const docFunction = resource.type ===
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
const resource = findFunctionInBlueprint(parsedBlueprint, fnName); // throws if not found
|
|
34
|
+
const docFunction = resource.type === SANITY_FUNCTION_DOCUMENT;
|
|
35
|
+
const mediaFunction = resource.type === SANITY_FUNCTION_MEDIA_LIBRARY_ASSET;
|
|
36
|
+
const contextOptions = docFunction || mediaFunction
|
|
37
|
+
? {
|
|
38
|
+
clientOptions: {
|
|
39
|
+
apiVersion: api,
|
|
40
|
+
dataset,
|
|
41
|
+
projectId,
|
|
42
|
+
organizationId,
|
|
43
|
+
},
|
|
44
|
+
eventResourceType: docFunction ? 'dataset' : 'media-library',
|
|
45
|
+
eventResourceId: (docFunction ? `${projectId}.${dataset}` : mediaLibraryId) || '',
|
|
46
|
+
functionResourceType: 'project',
|
|
47
|
+
functionResourceId: projectId || '',
|
|
48
|
+
}
|
|
49
|
+
: {};
|
|
46
50
|
// If the user sets the flag to use the real token set it in our options
|
|
47
|
-
if (withUserToken) {
|
|
51
|
+
if (withUserToken && isGroqContextOptions(contextOptions)) {
|
|
48
52
|
contextOptions.clientOptions.token = config.token || undefined;
|
|
49
53
|
}
|
|
50
54
|
let before = null;
|
|
@@ -115,12 +119,16 @@ export async function functionTestCore(options) {
|
|
|
115
119
|
payload = buildPayload({ data, file });
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
|
-
const invokeOptions =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
const invokeOptions = docFunction || mediaFunction
|
|
123
|
+
? {
|
|
124
|
+
event: eventType,
|
|
125
|
+
payload,
|
|
126
|
+
before,
|
|
127
|
+
after,
|
|
128
|
+
}
|
|
129
|
+
: {
|
|
130
|
+
event: 'schedule',
|
|
131
|
+
};
|
|
124
132
|
const spinner = ora('Executing function...').start();
|
|
125
133
|
const { json, logs, error } = await testAction(resource, invokeOptions, contextOptions, {
|
|
126
134
|
timeout: timeout ? timeout : resource.timeout,
|
package/dist/server/app.js
CHANGED
|
@@ -5,7 +5,7 @@ import { WebSocketServer } from 'ws';
|
|
|
5
5
|
import { readLocalBlueprint } from '../actions/blueprints/blueprint.js';
|
|
6
6
|
import config from '../config.js';
|
|
7
7
|
import { isRecord } from '../utils/is-record.js';
|
|
8
|
-
import { isEventType, } from '../utils/types.js';
|
|
8
|
+
import { isEventType, isGroqContextOptions, } from '../utils/types.js';
|
|
9
9
|
import { handleInvokeRequest } from './handlers/invoke.js';
|
|
10
10
|
const app = (host, port, executionOptions) => {
|
|
11
11
|
const requestListener = async (req, res) => {
|
|
@@ -34,11 +34,13 @@ const app = (host, port, executionOptions) => {
|
|
|
34
34
|
const { data, func: functionName, metadata } = parseInvokeRequest(Buffer.concat(body));
|
|
35
35
|
const { context, event } = data;
|
|
36
36
|
// replace user token if required
|
|
37
|
-
if (context
|
|
38
|
-
context.clientOptions.token
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
if (isGroqContextOptions(context)) {
|
|
38
|
+
if (context.clientOptions.token) {
|
|
39
|
+
context.clientOptions.token = config.token || undefined;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
delete context.clientOptions.token;
|
|
43
|
+
}
|
|
42
44
|
}
|
|
43
45
|
const result = await handleInvokeRequest(functionName, event, metadata, context, executionOptions);
|
|
44
46
|
// Add Server-Timing header
|
|
@@ -324,21 +326,30 @@ function parseInvokeRequest(body) {
|
|
|
324
326
|
throw new Error('Request body is not valid, `metadata.event` field is missing');
|
|
325
327
|
}
|
|
326
328
|
const metadataEvent = metadata.event;
|
|
327
|
-
if (typeof metadataEvent !== 'string' ||
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (!('before' in metadata)) {
|
|
331
|
-
throw new Error('Request body is not valid, `metadata.before` field is missing');
|
|
332
|
-
}
|
|
333
|
-
if (!('after' in metadata)) {
|
|
334
|
-
throw new Error('Request body is not valid, `metadata.after` field is missing');
|
|
335
|
-
}
|
|
336
|
-
const { before, after } = metadata;
|
|
337
|
-
if (!isRecord(before) && before !== null) {
|
|
338
|
-
throw new Error('Request body is not valid, `metadata.before` field is not an object');
|
|
329
|
+
if (typeof metadataEvent !== 'string' ||
|
|
330
|
+
(!isEventType(metadataEvent) && metadataEvent !== 'schedule')) {
|
|
331
|
+
throw new Error('Request body is not valid, `metadata.event` field is not one of `create`, `update`, `delete`, or `schedule`');
|
|
339
332
|
}
|
|
340
|
-
|
|
341
|
-
|
|
333
|
+
let before = null;
|
|
334
|
+
let after = null;
|
|
335
|
+
// Only GROQ-based events (create, update, delete) have before and after fields
|
|
336
|
+
if (metadataEvent !== 'schedule') {
|
|
337
|
+
if (!('before' in metadata)) {
|
|
338
|
+
throw new Error('Request body is not valid, `metadata.before` field is missing');
|
|
339
|
+
}
|
|
340
|
+
if (!('after' in metadata)) {
|
|
341
|
+
throw new Error('Request body is not valid, `metadata.after` field is missing');
|
|
342
|
+
}
|
|
343
|
+
const beforeValue = metadata.before;
|
|
344
|
+
const afterValue = metadata.after;
|
|
345
|
+
if (!isRecord(beforeValue) && beforeValue !== null) {
|
|
346
|
+
throw new Error('Request body is not valid, `metadata.before` field is not an object');
|
|
347
|
+
}
|
|
348
|
+
if (!isRecord(afterValue) && afterValue !== null) {
|
|
349
|
+
throw new Error('Request body is not valid, `metadata.after` field is not an object');
|
|
350
|
+
}
|
|
351
|
+
before = beforeValue;
|
|
352
|
+
after = afterValue;
|
|
342
353
|
}
|
|
343
354
|
const clientOptions = {
|
|
344
355
|
...context.clientOptions,
|
|
@@ -361,7 +372,7 @@ function parseInvokeRequest(body) {
|
|
|
361
372
|
},
|
|
362
373
|
event,
|
|
363
374
|
},
|
|
364
|
-
metadata: { event: metadataEvent, before, after },
|
|
375
|
+
metadata: metadataEvent === 'schedule' ? { event: metadataEvent } : { event: metadataEvent, before, after },
|
|
365
376
|
};
|
|
366
377
|
}
|
|
367
378
|
export { app, buildApiUrl, parseDocumentUrl };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InvocationResponse, InvokeContextOptions, InvokeExecutionOptions,
|
|
2
|
-
export declare function handleInvokeRequest(functionName: string, event: Record<string, unknown>, metadata:
|
|
1
|
+
import type { InvocationResponse, InvokeContextOptions, InvokeExecutionOptions, InvokePayloadMetadata } from '../../utils/types.js';
|
|
2
|
+
export declare function handleInvokeRequest(functionName: string, event: Record<string, unknown>, metadata: InvokePayloadMetadata, context: InvokeContextOptions, executionOptions?: Partial<InvokeExecutionOptions>): Promise<InvocationResponse & {
|
|
3
3
|
timings: Record<string, number>;
|
|
4
4
|
}>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { readLocalBlueprint } from '../../actions/blueprints/blueprint.js';
|
|
2
|
-
import {
|
|
2
|
+
import { findFunctionInBlueprint } from '../../utils/find-function.js';
|
|
3
3
|
import invoke from '../../utils/invoke-local.js';
|
|
4
4
|
export async function handleInvokeRequest(functionName, event, metadata, context, executionOptions) {
|
|
5
5
|
const start = performance.now();
|
|
6
6
|
const { parsedBlueprint } = await readLocalBlueprint();
|
|
7
|
-
const resource =
|
|
7
|
+
const resource = findFunctionInBlueprint(parsedBlueprint, functionName);
|
|
8
8
|
const readBlueprintTime = performance.now() - start;
|
|
9
9
|
const payload = {
|
|
10
10
|
payload: event,
|
|
@@ -7,5 +7,8 @@ export class ApiBaseElement extends HTMLElement {
|
|
|
7
7
|
constructor() {
|
|
8
8
|
super()
|
|
9
9
|
this.api = api
|
|
10
|
+
this.SANITY_FUNCTION_DOCUMENT = 'sanity.function.document'
|
|
11
|
+
this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET = 'sanity.function.media-library.asset'
|
|
12
|
+
this.SANITY_FUNCTION_SCHEDULE = 'sanity.function.cron'
|
|
10
13
|
}
|
|
11
14
|
}
|