@sanity/runtime-cli 17.10.0 → 17.11.1-next.gc1820a0
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 +20 -20
- package/dist/actions/blueprints/blueprint.js +1 -1
- package/dist/actions/blueprints/pre-deploy.js +7 -6
- package/dist/actions/blueprints/wait-for-operation.js +2 -1
- package/dist/actions/sanity/examples.js +9 -2
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/cores/blueprints/config.js +18 -8
- package/dist/cores/blueprints/deploy.js +9 -8
- package/dist/cores/blueprints/destroy.js +7 -6
- package/dist/cores/blueprints/doctor.js +15 -2
- package/dist/cores/blueprints/init.d.ts +2 -0
- package/dist/cores/blueprints/init.js +56 -20
- package/dist/cores/blueprints/plan.js +11 -6
- package/dist/cores/blueprints/promote.js +3 -2
- package/dist/cores/functions/add.js +11 -7
- package/dist/cores/functions/env/add.js +2 -1
- package/dist/cores/functions/logs.js +50 -6
- package/dist/cores/index.d.ts +5 -0
- package/dist/server/static/vendor/vendor.bundle.js +13 -6
- package/dist/utils/blueprints/hints.js +8 -3
- package/dist/utils/display/prompt.d.ts +15 -2
- package/dist/utils/display/prompt.js +70 -8
- package/dist/utils/package-manager.d.ts +20 -0
- package/dist/utils/package-manager.js +63 -0
- package/dist/utils/types.d.ts +9 -0
- package/oclif.manifest.json +1 -1
- package/package.json +21 -22
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { cwd } from 'node:process';
|
|
4
4
|
import { confirm } from '@inquirer/prompts';
|
|
5
5
|
import { findBlueprintFile, readLocalBlueprint, writeBlueprintToDisk, } from '../../actions/blueprints/blueprint.js';
|
|
@@ -11,8 +11,9 @@ import { verifyExampleExists, writeExample } from '../../actions/sanity/examples
|
|
|
11
11
|
import { getProject } from '../../actions/sanity/projects.js';
|
|
12
12
|
import { BLUEPRINT_CONFIG_DIR, BLUEPRINT_CONFIG_FILE } from '../../config.js';
|
|
13
13
|
import { check, filePathRelativeToCwd, labeledId, warn } from '../../utils/display/presenters.js';
|
|
14
|
-
import { promptForBlueprintType, promptForOrganization, promptForProjectInOrg, promptForStack, runScopeAndStackWizard, } from '../../utils/display/prompt.js';
|
|
14
|
+
import { CancelledError, promptForBlueprintType, promptForInitLocation, promptForOrganization, promptForProjectInOrg, promptForStack, runScopeAndStackWizard, } from '../../utils/display/prompt.js';
|
|
15
15
|
import { isInteractive } from '../../utils/interactive.js';
|
|
16
|
+
import { installCommand, runCommand } from '../../utils/package-manager.js';
|
|
16
17
|
import { styleText } from '../../utils/style-text.js';
|
|
17
18
|
import { blueprintConfigCore } from './config.js';
|
|
18
19
|
/** Derive scope type and ID from mutually exclusive flag values. */
|
|
@@ -29,8 +30,8 @@ export async function blueprintInitCore(options) {
|
|
|
29
30
|
const { dir: argDir } = args;
|
|
30
31
|
const explicitDir = argDir || flagDir;
|
|
31
32
|
const isHere = !explicitDir || explicitDir === '.' || explicitDir === './';
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
let blueprintDir = isHere ? '.' : explicitDir;
|
|
34
|
+
let userProvidedDirName = isHere ? undefined : explicitDir;
|
|
34
35
|
const validationError = validateFlags({
|
|
35
36
|
stackId: flagStackId,
|
|
36
37
|
stackName: flagStackName,
|
|
@@ -43,7 +44,7 @@ export async function blueprintInitCore(options) {
|
|
|
43
44
|
const existingBlueprintFile = findBlueprintFile(blueprintDir);
|
|
44
45
|
// existing blueprint file on disk → configure it for deployment
|
|
45
46
|
if (existingBlueprintFile) {
|
|
46
|
-
|
|
47
|
+
const result = await handleExistingBlueprint({
|
|
47
48
|
existingBlueprintFile,
|
|
48
49
|
bin,
|
|
49
50
|
log,
|
|
@@ -56,6 +57,11 @@ export async function blueprintInitCore(options) {
|
|
|
56
57
|
flagStackName,
|
|
57
58
|
verbose,
|
|
58
59
|
});
|
|
60
|
+
// the config core owns the wizard, but the user ran `init`
|
|
61
|
+
if (!result.success && result.cancelled) {
|
|
62
|
+
return { success: false, error: 'Initialization cancelled.', cancelled: true };
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
59
65
|
}
|
|
60
66
|
// --example flag → scaffold from an example template
|
|
61
67
|
if (flagExample) {
|
|
@@ -70,13 +76,21 @@ export async function blueprintInitCore(options) {
|
|
|
70
76
|
}
|
|
71
77
|
// no blueprint on disk → create new blueprint files from scratch
|
|
72
78
|
if (!explicitDir && isInteractive) {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
const chosenDir = await promptForInitLocation(cwd());
|
|
80
|
+
if (chosenDir) {
|
|
81
|
+
blueprintDir = chosenDir;
|
|
82
|
+
userProvidedDirName = chosenDir;
|
|
83
|
+
// findBlueprintFile walks up, so only refuse on a manifest in the folder itself
|
|
84
|
+
const existing = findBlueprintFile(chosenDir);
|
|
85
|
+
if (existing && dirname(resolve(existing.blueprintFilePath)) === resolve(chosenDir)) {
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
error: `A Blueprint already exists in "${chosenDir}".`,
|
|
89
|
+
suggestions: [
|
|
90
|
+
`Run \`${runCommand(bin)} blueprints init ${chosenDir}\` to configure it for deployment.`,
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
80
94
|
}
|
|
81
95
|
}
|
|
82
96
|
return handleFreshInitialization({
|
|
@@ -94,6 +108,9 @@ export async function blueprintInitCore(options) {
|
|
|
94
108
|
});
|
|
95
109
|
}
|
|
96
110
|
catch (error) {
|
|
111
|
+
if (error instanceof CancelledError) {
|
|
112
|
+
return { success: false, error: 'Initialization cancelled.', cancelled: true };
|
|
113
|
+
}
|
|
97
114
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
98
115
|
return { success: false, error: errorMessage };
|
|
99
116
|
}
|
|
@@ -156,6 +173,7 @@ async function handleFreshInitialization(options) {
|
|
|
156
173
|
stackId: flagStackId,
|
|
157
174
|
stackName: flagStackName,
|
|
158
175
|
knownProjectId,
|
|
176
|
+
blueprintDir,
|
|
159
177
|
log,
|
|
160
178
|
token,
|
|
161
179
|
});
|
|
@@ -241,7 +259,7 @@ async function handleExampleInitialization(options) {
|
|
|
241
259
|
const { blueprintFilePath } = discoveredBlueprint;
|
|
242
260
|
writeConfigFile(blueprintFilePath, { projectId: resolvedProjectId, stackId: stack.id });
|
|
243
261
|
log(check(`${styleText('bold', 'Configured:')} ${exampleDir}/${BLUEPRINT_CONFIG_DIR}/${BLUEPRINT_CONFIG_FILE}`));
|
|
244
|
-
log(`\n Run "${styleText(['bold', 'magenta'], `cd ${exampleDir} &&
|
|
262
|
+
log(`\n Run "${styleText(['bold', 'magenta'], `cd ${exampleDir} && ${installCommand(exampleDir)}`)}" and check out the README`);
|
|
245
263
|
if (instructions) {
|
|
246
264
|
log('');
|
|
247
265
|
log(instructions);
|
|
@@ -251,6 +269,7 @@ async function handleExampleInitialization(options) {
|
|
|
251
269
|
export async function resolveScopeAndStack(params) {
|
|
252
270
|
const { projectId, organizationId, stackId, stackName, knownProjectId, log, token } = params;
|
|
253
271
|
const flagScope = resolveScopeFromFlags(projectId, organizationId);
|
|
272
|
+
const suggestedName = params.blueprintDir ? basename(resolve(params.blueprintDir)) : undefined;
|
|
254
273
|
// --- Branch 1: Scope provided via flags ---
|
|
255
274
|
if (flagScope) {
|
|
256
275
|
const { scopeType, scopeId } = flagScope;
|
|
@@ -283,7 +302,13 @@ export async function resolveScopeAndStack(params) {
|
|
|
283
302
|
// No stack flag: prompt for stack selection
|
|
284
303
|
if (!resolvedStackId) {
|
|
285
304
|
log('\nBlueprints are deployed to a "Stack".');
|
|
286
|
-
const stackResult = await promptForStack({
|
|
305
|
+
const stackResult = await promptForStack({
|
|
306
|
+
scopeType,
|
|
307
|
+
scopeId,
|
|
308
|
+
token,
|
|
309
|
+
logger: log,
|
|
310
|
+
suggestedName,
|
|
311
|
+
});
|
|
287
312
|
if (typeof stackResult === 'symbol')
|
|
288
313
|
throw new Error('Unexpected back navigation');
|
|
289
314
|
resolvedStackId = stackResult.stackId;
|
|
@@ -315,6 +340,7 @@ export async function resolveScopeAndStack(params) {
|
|
|
315
340
|
scopeId: knownProjectId,
|
|
316
341
|
token,
|
|
317
342
|
logger: log,
|
|
343
|
+
suggestedName,
|
|
318
344
|
});
|
|
319
345
|
if (typeof stackResult === 'symbol')
|
|
320
346
|
throw new Error('Unexpected back navigation');
|
|
@@ -323,7 +349,11 @@ export async function resolveScopeAndStack(params) {
|
|
|
323
349
|
}
|
|
324
350
|
}
|
|
325
351
|
// Full wizard: org -> scope -> project (if needed) -> stack
|
|
326
|
-
const wizard = await runScopeAndStackWizard({
|
|
352
|
+
const wizard = await runScopeAndStackWizard({
|
|
353
|
+
token,
|
|
354
|
+
logger: log,
|
|
355
|
+
suggestedStackName: suggestedName,
|
|
356
|
+
});
|
|
327
357
|
return { scopeType: wizard.scopeType, scopeId: wizard.scopeId, stackId: wizard.stackId };
|
|
328
358
|
}
|
|
329
359
|
export async function determineBlueprintExtension(params) {
|
|
@@ -386,12 +416,18 @@ export async function createBlueprintFiles(params) {
|
|
|
386
416
|
log.warn(warn(`Unable to add ${blueprintsPackage} to your project.`));
|
|
387
417
|
}
|
|
388
418
|
}
|
|
389
|
-
const
|
|
419
|
+
const runLine = (command, purpose) => ` Run "${styleText(['bold', 'magenta'], command)}" ${purpose}`;
|
|
420
|
+
// project setup, only when there is something to do
|
|
421
|
+
const setupParts = [];
|
|
390
422
|
if (userProvidedDirName)
|
|
391
|
-
|
|
423
|
+
setupParts.push(`cd ${userProvidedDirName}`);
|
|
392
424
|
if (blueprintExtension !== 'json')
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
425
|
+
setupParts.push(installCommand(blueprintDir));
|
|
426
|
+
log('');
|
|
427
|
+
if (setupParts.length > 0)
|
|
428
|
+
log(runLine(setupParts.join(' && '), 'first'));
|
|
429
|
+
// the manifest starts empty, so name the command that fills it
|
|
430
|
+
log(runLine(`${runCommand(bin)} functions add`, 'to add a Function'));
|
|
431
|
+
log(runLine(`${runCommand(bin)} blueprints --help`, 'to see every command'));
|
|
396
432
|
return { success: true };
|
|
397
433
|
}
|
|
@@ -3,6 +3,7 @@ import { planStack, resolveStackIdByNameOrId } from '../../actions/blueprints/st
|
|
|
3
3
|
import { extractDurableConfig } from '../../utils/blueprints/extract-durable-config.js';
|
|
4
4
|
import { createHintCollector } from '../../utils/blueprints/hints.js';
|
|
5
5
|
import { formatDeploymentPlan, formatResourceTree, hasActionableChanges, } from '../../utils/display/blueprints-formatting.js';
|
|
6
|
+
import { runCommand } from '../../utils/package-manager.js';
|
|
6
7
|
import { styleText } from '../../utils/style-text.js';
|
|
7
8
|
import { getWarnings } from '../../utils/warnings.js';
|
|
8
9
|
export async function blueprintPlanCore(options) {
|
|
@@ -18,12 +19,14 @@ export async function blueprintPlanCore(options) {
|
|
|
18
19
|
? {
|
|
19
20
|
success: false,
|
|
20
21
|
error: 'Missing authentication token',
|
|
21
|
-
suggestions: [`Run
|
|
22
|
+
suggestions: [`Run \`${runCommand(bin)} login\` to authenticate.`],
|
|
22
23
|
}
|
|
23
24
|
: {
|
|
24
25
|
success: false,
|
|
25
26
|
error: 'Missing Blueprint configuration',
|
|
26
|
-
suggestions: [
|
|
27
|
+
suggestions: [
|
|
28
|
+
`Run \`${runCommand(bin)} blueprints doctor --fix\` to fix your configuration.`,
|
|
29
|
+
],
|
|
27
30
|
};
|
|
28
31
|
}
|
|
29
32
|
const stackId = flags.stack
|
|
@@ -34,7 +37,9 @@ export async function blueprintPlanCore(options) {
|
|
|
34
37
|
return {
|
|
35
38
|
success: false,
|
|
36
39
|
error: 'Missing Stack deployment configuration',
|
|
37
|
-
suggestions: [
|
|
40
|
+
suggestions: [
|
|
41
|
+
`Run \`${runCommand(bin)} blueprints doctor --fix\` to fix your configuration.`,
|
|
42
|
+
],
|
|
38
43
|
};
|
|
39
44
|
}
|
|
40
45
|
const auth = { token, scopeType, scopeId };
|
|
@@ -69,7 +74,7 @@ export async function blueprintPlanCore(options) {
|
|
|
69
74
|
error: 'Deployment plan has problems',
|
|
70
75
|
suggestions: [
|
|
71
76
|
...hints.getSuggestions(),
|
|
72
|
-
`Fix the issues above, then run
|
|
77
|
+
`Fix the issues above, then run \`${runCommand(bin)} blueprints plan\` again.`,
|
|
73
78
|
],
|
|
74
79
|
};
|
|
75
80
|
}
|
|
@@ -77,10 +82,10 @@ export async function blueprintPlanCore(options) {
|
|
|
77
82
|
log('');
|
|
78
83
|
log(formatDeploymentPlan(planResponse.deploymentPlan));
|
|
79
84
|
if (hasActionableChanges(planResponse.deploymentPlan)) {
|
|
80
|
-
log(`\n Run "${styleText(['bold', 'magenta'],
|
|
85
|
+
log(`\n Run "${styleText(['bold', 'magenta'], `${runCommand(bin)} blueprints deploy`)}" to apply these Stack changes`);
|
|
81
86
|
}
|
|
82
87
|
else {
|
|
83
|
-
log(`\n ${styleText('dim', `No significant changes to deploy. Run
|
|
88
|
+
log(`\n ${styleText('dim', `No significant changes to deploy. Run \`${runCommand(bin)} blueprints deploy\` to apply.`)}`);
|
|
84
89
|
}
|
|
85
90
|
return {
|
|
86
91
|
success: true,
|
|
@@ -2,6 +2,7 @@ import { confirm } from '@inquirer/prompts';
|
|
|
2
2
|
import { patchConfigFile } from '../../actions/blueprints/config.js';
|
|
3
3
|
import { promoteStack } from '../../actions/blueprints/stacks.js';
|
|
4
4
|
import { niceId } from '../../utils/display/presenters.js';
|
|
5
|
+
import { runCommand } from '../../utils/package-manager.js';
|
|
5
6
|
export async function blueprintPromoteCore(options) {
|
|
6
7
|
const { bin = 'sanity', log, stackId, auth, flags, deployedStack, blueprint } = options;
|
|
7
8
|
const newStackName = flags['new-stack-name'];
|
|
@@ -47,7 +48,7 @@ export async function blueprintPromoteCore(options) {
|
|
|
47
48
|
if (error && /access|permission|forbidden/i.test(error)) {
|
|
48
49
|
suggestions.push('Verify you have organization admin access.');
|
|
49
50
|
}
|
|
50
|
-
suggestions.push(`Run
|
|
51
|
+
suggestions.push(`Run \`${runCommand(bin)} blueprints info\` to check the current Stack scope.`);
|
|
51
52
|
return { success: false, error: error || 'Failed to promote Stack', suggestions };
|
|
52
53
|
}
|
|
53
54
|
spinner.stop().clear();
|
|
@@ -66,7 +67,7 @@ export async function blueprintPromoteCore(options) {
|
|
|
66
67
|
success: false,
|
|
67
68
|
error: 'Stack promoted, but could not update Blueprint config (.sanity/blueprint.config.json).',
|
|
68
69
|
suggestions: [
|
|
69
|
-
`Run
|
|
70
|
+
`Run \`${runCommand(bin)} blueprints config --edit\` to update your Blueprint config manually.`,
|
|
70
71
|
],
|
|
71
72
|
};
|
|
72
73
|
}
|
|
@@ -8,7 +8,9 @@ import { verifyExampleExists, writeExample } from '../../actions/sanity/examples
|
|
|
8
8
|
import { EVENT_DOCUMENT_CREATE, EVENT_DOCUMENT_DELETE, EVENT_DOCUMENT_UPDATE, EVENT_MEDIA_LIBRARY_ASSET_CREATE, EVENT_MEDIA_LIBRARY_ASSET_DELETE, EVENT_MEDIA_LIBRARY_ASSET_UPDATE, EVENT_PUBSUB, EVENT_SCHEDULED, EVENT_SYNC_TAG_INVALIDATE, FUNCTION_TYPES, LABEL_DOCUMENT_CREATE, LABEL_DOCUMENT_DELETE, LABEL_DOCUMENT_UPDATE, LABEL_MEDIA_LIBRARY_ASSET_CREATE, LABEL_MEDIA_LIBRARY_ASSET_DELETE, LABEL_MEDIA_LIBRARY_ASSET_UPDATE, LABEL_PUBSUB, LABEL_SCHEDULED, LABEL_SYNC_TAG_INVALIDATE, MAP_EVENT_TO_FUNCTION_TYPE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_PUBSUB, SANITY_FUNCTION_SCHEDULED, SANITY_FUNCTION_SYNC_TAG_INVALIDATE, } from '../../constants.js';
|
|
9
9
|
import { check, indent, warn } from '../../utils/display/presenters.js';
|
|
10
10
|
import { validateFunctionName } from '../../utils/functions/validate-name.js';
|
|
11
|
+
import { detectPackageManager } from '../../utils/package-manager.js';
|
|
11
12
|
import { styleText } from '../../utils/style-text.js';
|
|
13
|
+
import { INSTALLER_OPTIONS } from '../../utils/types.js';
|
|
12
14
|
import { getWarnings } from '../../utils/warnings.js';
|
|
13
15
|
export const generateFunctionBlueprintResourceTemplate = (fnName, eventNames) => {
|
|
14
16
|
const functionType = MAP_EVENT_TO_FUNCTION_TYPE[eventNames[0]];
|
|
@@ -44,7 +46,8 @@ export async function functionAddCore(options) {
|
|
|
44
46
|
const root = cwd();
|
|
45
47
|
const { log, blueprint, flags } = options;
|
|
46
48
|
const { blueprintFilePath } = blueprint.fileInfo;
|
|
47
|
-
const
|
|
49
|
+
const blueprintFileDir = dirname(blueprintFilePath);
|
|
50
|
+
const warnings = getWarnings({ dir: blueprintFileDir });
|
|
48
51
|
const { example: flagExample, name: flagResourceName, type: flagType, javascript: flagJs, helpers: flagHelpers, install: flagI, installer: flagInstaller, } = flags;
|
|
49
52
|
let { language: flagLang } = flags;
|
|
50
53
|
flagLang = flagJs ? 'js' : flagLang;
|
|
@@ -183,7 +186,7 @@ export async function functionAddCore(options) {
|
|
|
183
186
|
}
|
|
184
187
|
else {
|
|
185
188
|
addHelpers = flagHelpers || (await promptForAddHelpers());
|
|
186
|
-
installCommand = addHelpers ? await promptForInstallCommand() : null;
|
|
189
|
+
installCommand = addHelpers ? await promptForInstallCommand(blueprintFileDir) : null;
|
|
187
190
|
}
|
|
188
191
|
if (installCommand)
|
|
189
192
|
log(`${styleText('magenta', 'Installing')} with ${installCommand}...`);
|
|
@@ -286,16 +289,17 @@ async function promptForAddHelpers() {
|
|
|
286
289
|
});
|
|
287
290
|
return addHelpers;
|
|
288
291
|
}
|
|
289
|
-
async function promptForInstallCommand() {
|
|
292
|
+
async function promptForInstallCommand(dir) {
|
|
293
|
+
// hydrate only supports these three; a detected bun falls back to npm
|
|
294
|
+
const detected = detectPackageManager(dir);
|
|
295
|
+
const preferred = INSTALLER_OPTIONS.find((option) => option === detected) ?? 'npm';
|
|
290
296
|
const command = await select({
|
|
291
297
|
message: 'How to install the @sanity/functions helpers:',
|
|
292
298
|
choices: [
|
|
293
|
-
{ name:
|
|
294
|
-
{ name: 'pnpm', value: 'pnpm' },
|
|
295
|
-
{ name: 'yarn', value: 'yarn' },
|
|
299
|
+
...INSTALLER_OPTIONS.map((option) => ({ name: option, value: option })),
|
|
296
300
|
{ name: 'Skip install', value: null },
|
|
297
301
|
],
|
|
298
|
-
default:
|
|
302
|
+
default: preferred,
|
|
299
303
|
});
|
|
300
304
|
return command;
|
|
301
305
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { update } from '../../../actions/functions/env/update.js';
|
|
2
|
+
import { ENV_MASK } from '../../../constants.js';
|
|
2
3
|
import { findFunctionInStack } from '../../../utils/functions/find.js';
|
|
3
4
|
import { resolveFunctionAuth } from '../../../utils/functions/resolve-auth.js';
|
|
4
5
|
import { styleText } from '../../../utils/style-text.js';
|
|
@@ -17,5 +18,5 @@ export async function functionEnvAddCore(options) {
|
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
20
|
spinner.succeed(`Update of ${args.key} succeeded`);
|
|
20
|
-
return { success: true, json: { name: args.name, key: args.key, value:
|
|
21
|
+
return { success: true, json: { name: args.name, key: args.key, value: ENV_MASK } };
|
|
21
22
|
}
|
|
@@ -58,8 +58,7 @@ async function streamLogs({ name, externalId, auth, log, }) {
|
|
|
58
58
|
alreadyOpened = true;
|
|
59
59
|
};
|
|
60
60
|
const renderLog = (logEntry) => {
|
|
61
|
-
|
|
62
|
-
log(formatLog(time, level, message, true));
|
|
61
|
+
log(formatLog(logEntry, true));
|
|
63
62
|
};
|
|
64
63
|
streamLogsAction(externalId, auth, renderLog, onOpen, (error) => log.error(`${styleText('red', 'Error:')} ${error}`), log);
|
|
65
64
|
// Return a special key for streaming mode
|
|
@@ -92,27 +91,72 @@ async function getLogs({ name, externalId, auth, limit, json, utc, log, }) {
|
|
|
92
91
|
const logLength = filteredLogs.length;
|
|
93
92
|
log(`Found ${styleText('bold', logLength.toString())} log ${logLength === 1 ? 'entry' : 'entries'} for function ${styleText('yellow', name)}`);
|
|
94
93
|
log('\n');
|
|
95
|
-
for (const
|
|
96
|
-
log(formatLog(
|
|
94
|
+
for (const entry of filteredLogs) {
|
|
95
|
+
log(formatLog(entry, utc));
|
|
97
96
|
}
|
|
98
97
|
}
|
|
99
98
|
else {
|
|
100
|
-
|
|
99
|
+
const cleaned = filteredLogs.map(({ time, level, message, operationName, attempt, requestId, executionArn, }) => {
|
|
100
|
+
const isDurable = executionArn !== undefined;
|
|
101
|
+
const isStep = operationName !== undefined || attempt !== undefined;
|
|
102
|
+
const durableContext = isDurable ? (isStep ? 'step' : 'handler') : undefined;
|
|
103
|
+
return {
|
|
104
|
+
time,
|
|
105
|
+
level,
|
|
106
|
+
message,
|
|
107
|
+
requestId,
|
|
108
|
+
...(durableContext ? { durableContext } : {}),
|
|
109
|
+
...(operationName !== undefined ? { step: operationName } : {}),
|
|
110
|
+
...(attempt !== undefined ? { attempt } : {}),
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
return { success: true, json: { logs: cleaned } };
|
|
101
114
|
}
|
|
102
115
|
return { success: true };
|
|
103
116
|
}
|
|
104
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Formats a log entry for display in the console.
|
|
119
|
+
* @param entry
|
|
120
|
+
* @param utc
|
|
121
|
+
*/
|
|
122
|
+
function formatLog(entry, utc) {
|
|
123
|
+
const { time, level, message, operationName, attempt, executionArn } = entry;
|
|
105
124
|
const date = new Date(time);
|
|
106
125
|
const [dateString, timeString] = utc
|
|
107
126
|
? date.toISOString().slice(0, 19).split('T')
|
|
108
127
|
: [date.toLocaleDateString(), date.toLocaleTimeString()];
|
|
128
|
+
const durableAnnotation = durableLogAnnotation({ operationName, attempt, executionArn });
|
|
109
129
|
return [
|
|
110
130
|
styleText('bold', dateString),
|
|
111
131
|
styleText(['bold', 'blue'], timeString),
|
|
112
132
|
logLevel(level.toUpperCase()),
|
|
133
|
+
...(durableAnnotation ? [durableAnnotation] : []),
|
|
113
134
|
message,
|
|
114
135
|
].join(' ');
|
|
115
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Log annotation for durable functions.
|
|
139
|
+
* @param operationName
|
|
140
|
+
* @param attempt
|
|
141
|
+
* @param executionArn
|
|
142
|
+
*/
|
|
143
|
+
function durableLogAnnotation({ operationName, attempt, executionArn, }) {
|
|
144
|
+
/**
|
|
145
|
+
* executionArn is included on durable function logs, but not on regular function logs.
|
|
146
|
+
* If executionArn is defined, we can assume the log is from a durable function.
|
|
147
|
+
* If executionArn is defined and operationName and attempt are undefined, the logger is in the handler context
|
|
148
|
+
*/
|
|
149
|
+
const annotationParts = [
|
|
150
|
+
executionArn !== undefined && operationName === undefined && attempt === undefined
|
|
151
|
+
? 'handler'
|
|
152
|
+
: undefined,
|
|
153
|
+
...(operationName ? [operationName] : []),
|
|
154
|
+
attempt !== undefined ? `attempt:${attempt}` : undefined,
|
|
155
|
+
].filter((part) => part !== undefined);
|
|
156
|
+
return annotationParts.length > 0
|
|
157
|
+
? styleText('grey', `[${annotationParts.join('|')}]`)
|
|
158
|
+
: undefined;
|
|
159
|
+
}
|
|
116
160
|
function logLevel(level) {
|
|
117
161
|
switch (level) {
|
|
118
162
|
case 'ERROR':
|
package/dist/cores/index.d.ts
CHANGED
|
@@ -39,6 +39,11 @@ export type CoreResult = {
|
|
|
39
39
|
ref?: string;
|
|
40
40
|
/** A machine-readable error code. */
|
|
41
41
|
code?: string;
|
|
42
|
+
/**
|
|
43
|
+
* The user cancelled a prompt. A calling core reads it to keep its own message.
|
|
44
|
+
* Not printed: `code` would surface as `Code:` in the command output.
|
|
45
|
+
*/
|
|
46
|
+
cancelled?: true;
|
|
42
47
|
/** Overrides the process exit code (defaults to oclif's 2). */
|
|
43
48
|
exitCode?: number;
|
|
44
49
|
streaming?: never;
|
|
@@ -27598,17 +27598,23 @@ function prettyBytes(number, options) {
|
|
|
27598
27598
|
}
|
|
27599
27599
|
|
|
27600
27600
|
const localeOptions = buildLocaleOptions(options);
|
|
27601
|
+
const base = options.binary ? 1024 : 1000;
|
|
27601
27602
|
|
|
27602
27603
|
let exponent = 0;
|
|
27603
27604
|
if (number >= 1) {
|
|
27604
|
-
exponent = Math.min(Math.floor(options.binary ? log(number) / Math.log(
|
|
27605
|
+
exponent = Math.min(Math.floor(options.binary ? log(number) / Math.log(base) : log10(number) / 3), UNITS.length - 1);
|
|
27605
27606
|
}
|
|
27606
27607
|
|
|
27607
|
-
number = divide(number,
|
|
27608
|
+
number = divide(number, base ** exponent);
|
|
27608
27609
|
|
|
27609
27610
|
if (!localeOptions) {
|
|
27610
|
-
const
|
|
27611
|
-
number = Number(number.toPrecision(
|
|
27611
|
+
const minimumPrecision = Math.max(3, Math.floor(number).toString().length);
|
|
27612
|
+
number = Number(number.toPrecision(minimumPrecision));
|
|
27613
|
+
|
|
27614
|
+
if (number >= base && exponent < UNITS.length - 1) {
|
|
27615
|
+
number /= base;
|
|
27616
|
+
exponent += 1;
|
|
27617
|
+
}
|
|
27612
27618
|
}
|
|
27613
27619
|
|
|
27614
27620
|
const numberString = toLocaleString(number, options.locale, localeOptions);
|
|
@@ -27769,10 +27775,11 @@ function prettyMilliseconds(milliseconds, options) {
|
|
|
27769
27775
|
const roundedMilliseconds = millisecondsAndBelow >= 1
|
|
27770
27776
|
? Math.round(millisecondsAndBelow)
|
|
27771
27777
|
: Math.ceil(millisecondsAndBelow);
|
|
27778
|
+
const maximumMilliseconds = 1000 - (10 ** -millisecondsDecimalDigits);
|
|
27772
27779
|
|
|
27773
27780
|
const millisecondsString = millisecondsDecimalDigits
|
|
27774
|
-
? millisecondsAndBelow.toFixed(millisecondsDecimalDigits)
|
|
27775
|
-
: roundedMilliseconds;
|
|
27781
|
+
? Math.min(millisecondsAndBelow, maximumMilliseconds).toFixed(millisecondsDecimalDigits)
|
|
27782
|
+
: Math.min(roundedMilliseconds, maximumMilliseconds);
|
|
27776
27783
|
|
|
27777
27784
|
add(
|
|
27778
27785
|
Number.parseFloat(millisecondsString),
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runCommand } from '../package-manager.js';
|
|
2
|
+
const promoteToOrganization = (bin) => `Run \`${runCommand(bin)} blueprints promote\` to move from project to organization scope.`;
|
|
1
3
|
/**
|
|
2
4
|
* Patterns matched against ERROR/FATAL log entries during streaming operations
|
|
3
5
|
* like deploy and destroy. Each entry is a [regex, suggestionFactory] tuple.
|
|
@@ -13,9 +15,12 @@ export const HINT_PATTERNS = [
|
|
|
13
15
|
[
|
|
14
16
|
// Function requires organization scope
|
|
15
17
|
/must be attached to an organization scoped stack/,
|
|
16
|
-
(_match, bin) => [
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
(_match, bin) => [promoteToOrganization(bin)],
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
// Resource needs an organization the project-scoped Stack cannot supply
|
|
22
|
+
/[Cc]annot resolve organization scope/,
|
|
23
|
+
(_match, bin) => [promoteToOrganization(bin)],
|
|
19
24
|
],
|
|
20
25
|
];
|
|
21
26
|
/**
|
|
@@ -3,6 +3,16 @@ import type { Logger } from '../logger.js';
|
|
|
3
3
|
import type { ScopeType } from '../types.js';
|
|
4
4
|
export declare const BACK: unique symbol;
|
|
5
5
|
export type Back = typeof BACK;
|
|
6
|
+
/** Thrown when the user picks "Cancel" in a prompt. Cores translate it to a clean result. */
|
|
7
|
+
export declare class CancelledError extends Error {
|
|
8
|
+
constructor(message?: string);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Ask where a new Blueprint should go.
|
|
12
|
+
* Returns the chosen directory, or null to use the current one.
|
|
13
|
+
* @throws CancelledError when the user cancels.
|
|
14
|
+
*/
|
|
15
|
+
export declare function promptForInitLocation(currentDir: string): Promise<string | null>;
|
|
6
16
|
export declare function promptForBlueprintType(): Promise<string>;
|
|
7
17
|
/**
|
|
8
18
|
* Fetches organizations and prompts the user to pick one (if multiple).
|
|
@@ -48,12 +58,14 @@ export declare function promptForProjectInOrg({ projects, knownProjectId, logger
|
|
|
48
58
|
* Prompt the user for a Stack after scope has been determined.
|
|
49
59
|
* Can create a new Stack or select an existing one.
|
|
50
60
|
*/
|
|
51
|
-
export declare function promptForStack({ scopeType, scopeId, token, logger, allowBack, }: {
|
|
61
|
+
export declare function promptForStack({ scopeType, scopeId, token, logger, allowBack, suggestedName, }: {
|
|
52
62
|
scopeType: ScopeType;
|
|
53
63
|
scopeId: string;
|
|
54
64
|
token: string;
|
|
55
65
|
logger: Logger;
|
|
56
66
|
allowBack?: boolean;
|
|
67
|
+
/** Prefilled name for a new Stack, usually the project directory name. */
|
|
68
|
+
suggestedName?: string;
|
|
57
69
|
}): Promise<{
|
|
58
70
|
stackId: string;
|
|
59
71
|
name: string;
|
|
@@ -62,11 +74,12 @@ export declare function promptForStack({ scopeType, scopeId, token, logger, allo
|
|
|
62
74
|
* Full wizard: Organization -> Scope -> Project (if project-scoped) -> Stack.
|
|
63
75
|
* Supports back-navigation between steps.
|
|
64
76
|
*/
|
|
65
|
-
export declare function runScopeAndStackWizard({ token, knownOrganizationId, knownProjectId, logger, }: {
|
|
77
|
+
export declare function runScopeAndStackWizard({ token, knownOrganizationId, knownProjectId, logger, suggestedStackName, }: {
|
|
66
78
|
token: string;
|
|
67
79
|
knownOrganizationId?: string;
|
|
68
80
|
knownProjectId?: string;
|
|
69
81
|
logger: Logger;
|
|
82
|
+
suggestedStackName?: string;
|
|
70
83
|
}): Promise<{
|
|
71
84
|
scopeType: ScopeType;
|
|
72
85
|
scopeId: string;
|