@sanity/runtime-cli 1.2.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -8
- package/dist/actions/blueprints/logs.d.ts +7 -0
- package/dist/actions/blueprints/logs.js +79 -0
- package/dist/actions/blueprints/operations.d.ts +18 -0
- package/dist/actions/blueprints/operations.js +52 -0
- package/dist/actions/blueprints/read-blueprint.d.ts +6 -1
- package/dist/actions/blueprints/read-blueprint.js +35 -9
- package/dist/actions/blueprints/stacks.d.ts +33 -5
- package/dist/actions/blueprints/stacks.js +53 -17
- package/dist/actions/blueprints/stash-asset.d.ts +4 -1
- package/dist/actions/blueprints/stash-asset.js +12 -7
- package/dist/actions/functions/invoke.js +8 -5
- package/dist/commands/blueprints/deploy.js +29 -35
- package/dist/commands/blueprints/info.js +45 -39
- package/dist/commands/blueprints/logs.d.ts +10 -0
- package/dist/commands/blueprints/logs.js +166 -0
- package/dist/commands/blueprints/plan.js +10 -23
- package/dist/config.js +2 -2
- package/dist/server/app.js +11 -6
- package/dist/server/static/api.js +4 -1
- package/dist/server/static/components/function-list.js +17 -12
- package/dist/utils/display/blueprints-formatting.d.ts +4 -0
- package/dist/utils/display/blueprints-formatting.js +42 -0
- package/dist/utils/display/colors.d.ts +6 -0
- package/dist/utils/display/colors.js +18 -0
- package/dist/utils/display/dates.d.ts +2 -0
- package/dist/utils/display/dates.js +26 -0
- package/dist/utils/types.d.ts +22 -3
- package/oclif.manifest.json +45 -1
- package/package.json +4 -2
|
@@ -1,68 +1,74 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
|
|
3
|
+
import { getStackByName } from '../../actions/blueprints/stacks.js';
|
|
4
|
+
import { formatResourceTree, formatStacksList, formatTitle, } from '../../utils/display/blueprints-formatting.js';
|
|
5
|
+
import { bold, green, red, yellow } from '../../utils/display/colors.js';
|
|
6
|
+
import { formatDate, formatDuration } from '../../utils/display/dates.js';
|
|
4
7
|
import Spinner from '../../utils/spinner.js';
|
|
5
8
|
export default class Info extends Command {
|
|
6
9
|
static description = 'Show information about a Blueprint';
|
|
7
10
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
8
11
|
async run() {
|
|
9
|
-
const {
|
|
12
|
+
const { document } = readBlueprintOnDisk();
|
|
13
|
+
const { displayName, name, projectId } = document;
|
|
10
14
|
const s = new Spinner();
|
|
11
15
|
if (!projectId) {
|
|
12
|
-
this.log(`
|
|
16
|
+
this.log(`Cannot determine project for Blueprint "${displayName}"`);
|
|
13
17
|
return;
|
|
14
18
|
}
|
|
15
19
|
s.start(`Retrieving info for "${displayName}"`);
|
|
16
20
|
try {
|
|
17
|
-
const { ok,
|
|
21
|
+
const { ok, stack, stackId: foundStackId, error, availableStacks, } = await getStackByName({
|
|
22
|
+
name,
|
|
23
|
+
projectId,
|
|
24
|
+
});
|
|
18
25
|
if (!ok) {
|
|
19
|
-
s.stop('Failed to retrieve stacks
|
|
26
|
+
s.stop(`${red('Failed')} to retrieve stacks`);
|
|
20
27
|
this.log(`Error: ${error || 'Unknown error'}`);
|
|
21
28
|
return;
|
|
22
29
|
}
|
|
23
|
-
if (
|
|
24
|
-
s.stop(
|
|
30
|
+
if (!foundStackId || !stack) {
|
|
31
|
+
s.stop(`${red('Failed')} to find Stack for ${formatTitle('Blueprint', displayName)}`);
|
|
32
|
+
if (availableStacks && availableStacks.length > 0) {
|
|
33
|
+
this.log(formatStacksList(availableStacks));
|
|
34
|
+
}
|
|
25
35
|
return;
|
|
26
36
|
}
|
|
27
|
-
const foundStack =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
const foundStack = { id: foundStackId };
|
|
38
|
+
s.stop(`${formatTitle('Blueprint', displayName)} Info\n`);
|
|
39
|
+
this.log(`Stack name: ${bold(stack.name)}`);
|
|
40
|
+
this.log(`Stack ID: ${yellow(foundStack.id)}`);
|
|
41
|
+
if (stack.createdAt) {
|
|
42
|
+
this.log(`Created: ${formatDate(stack.createdAt)}`);
|
|
33
43
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.log(`├─ Functions (${functionResources.length})`);
|
|
42
|
-
for (const fn of functionResources) {
|
|
43
|
-
this.log(`│ ├─ ${fn.name} <${fn.externalId}>`);
|
|
44
|
-
}
|
|
44
|
+
if (stack.updatedAt) {
|
|
45
|
+
this.log(`Updated: ${formatDate(stack.updatedAt)}`);
|
|
46
|
+
}
|
|
47
|
+
if (stack.recentOperation) {
|
|
48
|
+
const operation = stack.recentOperation;
|
|
49
|
+
if (operation.id) {
|
|
50
|
+
this.log(`Recent Operation <${yellow(operation.id)}>:`);
|
|
45
51
|
}
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
52
|
+
if (operation.status) {
|
|
53
|
+
const operationColor = operation.status === 'COMPLETED' ? green : red;
|
|
54
|
+
const status = operation.status || 'UNKNOWN';
|
|
55
|
+
this.log(` Status: ${operationColor(status)}`);
|
|
51
56
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (stack.recentOperation.status !== 'COMPLETED') {
|
|
55
|
-
this.log('\nOperation Details:');
|
|
56
|
-
if (stack.recentOperation.error) {
|
|
57
|
-
this.log(`Error: ${stack.recentOperation.error}`);
|
|
57
|
+
if (operation.createdAt) {
|
|
58
|
+
this.log(` Started : ${formatDate(operation.createdAt)}`);
|
|
58
59
|
}
|
|
59
|
-
if (
|
|
60
|
-
this.log(`
|
|
60
|
+
if (operation.status === 'COMPLETED' && operation.completedAt && operation.createdAt) {
|
|
61
|
+
this.log(` Completed: ${formatDate(operation.completedAt)}`);
|
|
62
|
+
this.log(` Duration: ${yellow(formatDuration(operation.createdAt, operation.completedAt))}`);
|
|
61
63
|
}
|
|
62
64
|
}
|
|
65
|
+
this.log('');
|
|
66
|
+
if (stack?.resources) {
|
|
67
|
+
formatResourceTree(stack.resources, this.log.bind(this));
|
|
68
|
+
}
|
|
63
69
|
}
|
|
64
70
|
catch (err) {
|
|
65
|
-
s.stop('Failed to
|
|
71
|
+
s.stop('Failed to parse Stack info');
|
|
66
72
|
if (err instanceof Error) {
|
|
67
73
|
this.log(`Error: ${err.message}`);
|
|
68
74
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Logs extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
'stack-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
watch: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { getLogs, streamLogs } from '../../actions/blueprints/logs.js';
|
|
3
|
+
import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
|
|
4
|
+
import { getStackByName } from '../../actions/blueprints/stacks.js';
|
|
5
|
+
import config from '../../config.js';
|
|
6
|
+
import { formatStacksList, formatTitle } from '../../utils/display/blueprints-formatting.js';
|
|
7
|
+
import { blue, bold, green, red, yellow } from '../../utils/display/colors.js';
|
|
8
|
+
import Spinner from '../../utils/spinner.js';
|
|
9
|
+
export default class Logs extends Command {
|
|
10
|
+
static description = 'Display logs for a Blueprint stack';
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> <%= command.id %>',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> --stack-id <stack-id>',
|
|
14
|
+
'<%= config.bin %> <%= command.id %> --watch',
|
|
15
|
+
];
|
|
16
|
+
static flags = {
|
|
17
|
+
'stack-id': Flags.string({
|
|
18
|
+
char: 's',
|
|
19
|
+
description: 'Stack ID to fetch logs for (optional if running in a blueprint directory)',
|
|
20
|
+
required: false,
|
|
21
|
+
}),
|
|
22
|
+
watch: Flags.boolean({
|
|
23
|
+
char: 'w',
|
|
24
|
+
description: 'Watch for new logs (streaming mode)',
|
|
25
|
+
required: false,
|
|
26
|
+
default: false,
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
async run() {
|
|
30
|
+
const { flags } = await this.parse(Logs);
|
|
31
|
+
const s = new Spinner();
|
|
32
|
+
const watchMode = flags.watch;
|
|
33
|
+
try {
|
|
34
|
+
const { document } = readBlueprintOnDisk();
|
|
35
|
+
const { name, displayName, projectId } = document;
|
|
36
|
+
if (!projectId) {
|
|
37
|
+
this.log('Cannot determine project ID for this Blueprint');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let stackId = flags['stack-id'];
|
|
41
|
+
if (!stackId) {
|
|
42
|
+
s.start(`Finding stack for blueprint "${displayName}"`);
|
|
43
|
+
const { ok, stackId: foundStackId, error, availableStacks, } = await getStackByName({
|
|
44
|
+
name,
|
|
45
|
+
projectId,
|
|
46
|
+
});
|
|
47
|
+
if (!ok) {
|
|
48
|
+
s.stop(`${red('Failed')} to retrieve stacks`);
|
|
49
|
+
this.log(`Error: ${error || 'Unknown error'}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!foundStackId) {
|
|
53
|
+
s.stop(`${red('Failed')} to find Stack for ${formatTitle('Blueprint', displayName)}`);
|
|
54
|
+
if (availableStacks && availableStacks.length > 0) {
|
|
55
|
+
this.log(formatStacksList(availableStacks));
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
stackId = foundStackId;
|
|
60
|
+
s.stop(`Found stack ${stackId} for blueprint "${displayName}"`);
|
|
61
|
+
}
|
|
62
|
+
if (!stackId) {
|
|
63
|
+
s.stop(`${red('Failed')} to determine stack ID`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (watchMode) {
|
|
67
|
+
// recent logs
|
|
68
|
+
s.start(`Fetching recent logs for stack ${stackId}`);
|
|
69
|
+
const { ok, logs, error } = await getLogs(stackId, projectId, config.token);
|
|
70
|
+
if (!ok) {
|
|
71
|
+
s.stop(`${red('Failed')} to retrieve logs`);
|
|
72
|
+
this.log(`Error: ${error || 'Unknown error'}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
s.stop(`${formatTitle('Blueprint', displayName)} ${bold('Live')} Logs`);
|
|
76
|
+
if (logs.length > 0) {
|
|
77
|
+
this.log('\nMost recent logs:');
|
|
78
|
+
const sortedLogs = [...logs].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
79
|
+
const recentLogs = sortedLogs.slice(-10);
|
|
80
|
+
for (const log of recentLogs) {
|
|
81
|
+
const date = new Date(log.timestamp);
|
|
82
|
+
const time = date.toLocaleTimeString();
|
|
83
|
+
const day = date.toLocaleDateString();
|
|
84
|
+
this.log(` ${bold(day)} ${yellow(time)} ${log.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.log(`No recent logs found for stack ${yellow(stackId)}`);
|
|
89
|
+
}
|
|
90
|
+
const onOpen = () => {
|
|
91
|
+
this.log(`\n${bold('Watching')} for new logs...`);
|
|
92
|
+
this.log(`Press ${bold('Ctrl+C')} to stop\n`);
|
|
93
|
+
};
|
|
94
|
+
let newestTimestamp = 0;
|
|
95
|
+
if (logs.length > 0) {
|
|
96
|
+
for (const log of logs) {
|
|
97
|
+
const timestamp = new Date(log.timestamp).getTime();
|
|
98
|
+
if (timestamp > newestTimestamp) {
|
|
99
|
+
newestTimestamp = timestamp;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const renderLog = (log) => {
|
|
104
|
+
const logTimestamp = new Date(log.timestamp).getTime();
|
|
105
|
+
if (logTimestamp <= newestTimestamp) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
newestTimestamp = logTimestamp;
|
|
109
|
+
const date = new Date(log.timestamp);
|
|
110
|
+
const time = date.toLocaleTimeString();
|
|
111
|
+
const day = date.toLocaleDateString();
|
|
112
|
+
this.log(`${green('>')} ${bold(day)} ${yellow(time)} ${log.message}`);
|
|
113
|
+
};
|
|
114
|
+
this.debug(`${yellow('Debug:')} Connecting to streaming endpoint for stack ${stackId}...`);
|
|
115
|
+
streamLogs(stackId, projectId, config.token, renderLog, onOpen, (error) => this.log(`${red('Error:')} ${error}`));
|
|
116
|
+
return new Promise(() => {
|
|
117
|
+
// hold the line until the user terminates with Ctrl+C
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
s.start(`Fetching logs for stack ${stackId}`);
|
|
121
|
+
const { ok, logs, error } = await getLogs(stackId, projectId, config.token);
|
|
122
|
+
if (!ok) {
|
|
123
|
+
s.stop(`${red('Failed')} to retrieve logs`);
|
|
124
|
+
this.log(`Error: ${error || 'Unknown error'}`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (logs.length === 0) {
|
|
128
|
+
s.stop(`No logs found for stack ${stackId}`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
s.stop(`${formatTitle('Blueprint', displayName)} Logs`);
|
|
132
|
+
this.log(`Found ${bold(logs.length.toString())} log entries for stack ${yellow(stackId)}\n`);
|
|
133
|
+
const logsByDay = new Map();
|
|
134
|
+
for (const log of logs) {
|
|
135
|
+
const date = new Date(log.timestamp);
|
|
136
|
+
const day = date.toLocaleDateString();
|
|
137
|
+
if (!logsByDay.has(day)) {
|
|
138
|
+
logsByDay.set(day, []);
|
|
139
|
+
}
|
|
140
|
+
logsByDay.get(day)?.push(log);
|
|
141
|
+
}
|
|
142
|
+
const sortedDays = Array.from(logsByDay.keys()).sort((a, b) => {
|
|
143
|
+
return new Date(a).getTime() - new Date(b).getTime();
|
|
144
|
+
});
|
|
145
|
+
for (const day of sortedDays) {
|
|
146
|
+
this.log(`${blue('Date:')} ${bold(day)}`);
|
|
147
|
+
const dayLogs = logsByDay.get(day) || [];
|
|
148
|
+
dayLogs.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
149
|
+
for (const [i, log] of dayLogs.entries()) {
|
|
150
|
+
const date = new Date(log.timestamp);
|
|
151
|
+
const time = date.toLocaleTimeString();
|
|
152
|
+
const isLast = i === dayLogs.length - 1;
|
|
153
|
+
this.log(` ${isLast ? '└─' : '├─'} ${yellow(time)} ${log.message}`);
|
|
154
|
+
}
|
|
155
|
+
// new line between days
|
|
156
|
+
this.log('');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
s.stop('Failed to retrieve logs');
|
|
161
|
+
if (err instanceof Error) {
|
|
162
|
+
this.log(`Error: ${err.message}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -1,31 +1,18 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import
|
|
2
|
+
import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
|
|
3
|
+
import { formatResourceTree, formatTitle } from '../../utils/display/blueprints-formatting.js';
|
|
3
4
|
export default class Plan extends Command {
|
|
4
5
|
static description = 'Enumerate resources to be deployed - will not modify any resources';
|
|
5
6
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
6
7
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
this.log('
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.log(`│ ├─ ${fn.name}: ${fn.src}`);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
if (otherResources.length > 0) {
|
|
19
|
-
this.log(`└─Other (${otherResources.length})`);
|
|
20
|
-
for (const other of otherResources) {
|
|
21
|
-
this.log(` ├─ ${other.name}`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
if (resources.length === 0) {
|
|
25
|
-
this.log('No resources to deploy');
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
this.log('\nRun `blueprints deploy` to deploy these changes');
|
|
8
|
+
const { document, fileName } = readBlueprintOnDisk();
|
|
9
|
+
const { displayName, name, resources } = document;
|
|
10
|
+
this.log(`${formatTitle('Blueprint', displayName)} Plan\n`);
|
|
11
|
+
this.log(`Blueprint document: "${name}" (${fileName})`);
|
|
12
|
+
this.log('');
|
|
13
|
+
formatResourceTree(resources, this.log.bind(this));
|
|
14
|
+
if (resources.length > 0) {
|
|
15
|
+
this.log('\nRun `sanity blueprints deploy` to deploy these changes');
|
|
29
16
|
}
|
|
30
17
|
}
|
|
31
18
|
}
|
package/dist/config.js
CHANGED
|
@@ -9,8 +9,8 @@ const functionsUrls = {
|
|
|
9
9
|
default: 'http://localhost:4567',
|
|
10
10
|
};
|
|
11
11
|
const blueprintsUrls = {
|
|
12
|
-
production: 'https://api.sanity.io
|
|
13
|
-
staging: 'https://api.sanity.work
|
|
12
|
+
production: 'https://api.sanity.io',
|
|
13
|
+
staging: 'https://api.sanity.work',
|
|
14
14
|
default: 'http://localhost:4567',
|
|
15
15
|
};
|
|
16
16
|
const functionsUrl = new URL(functionsUrls[nodeEnv] ?? functionsUrls.default);
|
package/dist/server/app.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { cwd } from 'node:process';
|
|
4
2
|
import { default as mime } from 'mime-types';
|
|
3
|
+
import readBlueprintOnDisk from '../actions/blueprints/read-blueprint.js';
|
|
5
4
|
import invoke from '../utils/invoke-local.js';
|
|
6
5
|
import * as http from 'node:http';
|
|
7
6
|
const host = 'localhost';
|
|
@@ -10,10 +9,16 @@ const app = (port) => {
|
|
|
10
9
|
res.setHeader('Content-Type', 'application/json');
|
|
11
10
|
switch (req.url) {
|
|
12
11
|
case '/blueprint': {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
try {
|
|
13
|
+
const blueprint = readBlueprintOnDisk();
|
|
14
|
+
res.setHeader('Content-Type', 'application/json');
|
|
15
|
+
res.writeHead(200);
|
|
16
|
+
res.end(JSON.stringify(blueprint));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
res.writeHead(404);
|
|
20
|
+
res.end();
|
|
21
|
+
}
|
|
17
22
|
break;
|
|
18
23
|
}
|
|
19
24
|
case '/invoke': {
|
|
@@ -42,9 +42,12 @@ function blueprint() {
|
|
|
42
42
|
fetch('/blueprint')
|
|
43
43
|
.then((response) => response.json())
|
|
44
44
|
.then((json) => {
|
|
45
|
-
const functions = json?.resources.filter((resource) => resource.kind === 'function')
|
|
45
|
+
const functions = json?.document?.resources.filter((resource) => resource.kind === 'function')
|
|
46
46
|
|
|
47
47
|
store.functions = functions
|
|
48
48
|
store.selectedIndex = functions[0].src
|
|
49
49
|
})
|
|
50
|
+
.catch(() => {
|
|
51
|
+
store.functions = []
|
|
52
|
+
})
|
|
50
53
|
}
|
|
@@ -15,18 +15,23 @@ class FunctionList extends ApiBaseElement {
|
|
|
15
15
|
this.api.store.selectedIndex = event.srcElement.value
|
|
16
16
|
}
|
|
17
17
|
renderFunctions = () => {
|
|
18
|
-
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
if (this.api.store.functions.length > 0) {
|
|
19
|
+
this.list.innerHTML = this.api.store.functions
|
|
20
|
+
.map((func) => {
|
|
21
|
+
const selected = this.api.store.selectedIndex === func.src ? 'selected' : ''
|
|
22
|
+
return `<li class="pad-sm ${selected}">${func.name}</li>`
|
|
23
|
+
})
|
|
24
|
+
.join('')
|
|
25
|
+
this.select.innerHTML = this.api.store.functions
|
|
26
|
+
.map((func) => {
|
|
27
|
+
const selected = this.api.store.selectedIndex === func.src ? 'selected' : ''
|
|
28
|
+
return `<option value="${func.src}" ${selected}>${func.name}</option>`
|
|
29
|
+
})
|
|
30
|
+
.join('')
|
|
31
|
+
} else {
|
|
32
|
+
this.list.innerHTML = '<option class="pad-sm">No blueprint.json file found</li>'
|
|
33
|
+
this.select.innerHTML = '<option>No blueprint.json file found</option>'
|
|
34
|
+
}
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
connectedCallback() {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BlueprintResource } from '../types.js';
|
|
2
|
+
export declare function formatTitle(title: string, name: string): string;
|
|
3
|
+
export declare function formatStacksList(stacks: string[]): string;
|
|
4
|
+
export declare function formatResourceTree(resources: BlueprintResource[], logger: (msg: string) => void): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { blue, bold, boldnblue, green, yellow } from './colors.js';
|
|
2
|
+
export function formatTitle(title, name) {
|
|
3
|
+
return `${boldnblue(title)} ${bold(`"${name}"`)}`;
|
|
4
|
+
}
|
|
5
|
+
export function formatStacksList(stacks) {
|
|
6
|
+
if (!stacks || stacks.length === 0) {
|
|
7
|
+
return 'No stacks found';
|
|
8
|
+
}
|
|
9
|
+
let result = '\nFound Stacks:\n';
|
|
10
|
+
for (const name of stacks) {
|
|
11
|
+
result += ` ${name}\n`;
|
|
12
|
+
}
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
export function formatResourceTree(resources, logger) {
|
|
16
|
+
if (!resources || resources.length === 0) {
|
|
17
|
+
logger(' No resources in this stack');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
logger(`${blue('Stack Resources')} [${resources.length}]`);
|
|
21
|
+
const functionResources = resources.filter((r) => r.kind === 'function');
|
|
22
|
+
const otherResources = resources.filter((r) => r.kind !== 'function');
|
|
23
|
+
const hasOtherResources = otherResources.length > 0;
|
|
24
|
+
if (functionResources.length > 0) {
|
|
25
|
+
logger(` ${hasOtherResources ? '├─' : '└─'} ${bold('Functions')} [${functionResources.length}]`);
|
|
26
|
+
for (const [i, fn] of functionResources.entries()) {
|
|
27
|
+
const isLast = i === functionResources.length - 1;
|
|
28
|
+
const prefix = hasOtherResources ? '│' : ' ';
|
|
29
|
+
const connector = isLast ? '└─' : '├─';
|
|
30
|
+
const name = green(fn.displayName || fn.name);
|
|
31
|
+
const externalId = fn.externalId ? `Fn<${yellow(fn.externalId)}>` : '';
|
|
32
|
+
logger(` ${prefix} ${connector} "${name}" ${externalId}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (hasOtherResources) {
|
|
36
|
+
logger(` └─ ${bold('Other Resources')} [${otherResources.length}]`);
|
|
37
|
+
for (const [i, other] of otherResources.entries()) {
|
|
38
|
+
const isLast = i === otherResources.length - 1;
|
|
39
|
+
logger(` ${isLast ? '└─' : '├─'} "${yellow(other.displayName || other.name || other.src)}"`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function bold(str: string): string;
|
|
2
|
+
export declare function blue(str: string): string;
|
|
3
|
+
export declare function green(str: string): string;
|
|
4
|
+
export declare function red(str: string): string;
|
|
5
|
+
export declare function yellow(str: string): string;
|
|
6
|
+
export declare function boldnblue(str: string): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function bold(str) {
|
|
2
|
+
return `\x1b[1m${str}\x1b[0m`;
|
|
3
|
+
}
|
|
4
|
+
export function blue(str) {
|
|
5
|
+
return `\x1b[34m${str}\x1b[0m`;
|
|
6
|
+
}
|
|
7
|
+
export function green(str) {
|
|
8
|
+
return `\x1b[32m${str}\x1b[0m`;
|
|
9
|
+
}
|
|
10
|
+
export function red(str) {
|
|
11
|
+
return `\x1b[31m${str}\x1b[0m`;
|
|
12
|
+
}
|
|
13
|
+
export function yellow(str) {
|
|
14
|
+
return `\x1b[33m${str}\x1b[0m`;
|
|
15
|
+
}
|
|
16
|
+
export function boldnblue(str) {
|
|
17
|
+
return bold(blue(str));
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function formatDate(dateString) {
|
|
2
|
+
const date = new Date(dateString);
|
|
3
|
+
return date.toLocaleString();
|
|
4
|
+
}
|
|
5
|
+
export function formatDuration(startDate, endDate) {
|
|
6
|
+
const start = new Date(startDate).getTime();
|
|
7
|
+
const end = new Date(endDate).getTime();
|
|
8
|
+
if (start > end) {
|
|
9
|
+
return 'Invalid duration';
|
|
10
|
+
}
|
|
11
|
+
const durationMs = end - start;
|
|
12
|
+
if (durationMs < 1000) {
|
|
13
|
+
return `${durationMs}ms`;
|
|
14
|
+
}
|
|
15
|
+
if (durationMs < 60000) {
|
|
16
|
+
return `${Math.round(durationMs / 1000)}s`;
|
|
17
|
+
}
|
|
18
|
+
if (durationMs < 3600000) {
|
|
19
|
+
const minutes = Math.floor(durationMs / 60000);
|
|
20
|
+
const seconds = Math.floor((durationMs % 60000) / 1000);
|
|
21
|
+
return `${minutes}m ${seconds}s`;
|
|
22
|
+
}
|
|
23
|
+
const hours = Math.floor(durationMs / 3600000);
|
|
24
|
+
const minutes = Math.floor((durationMs % 3600000) / 60000);
|
|
25
|
+
return `${hours}h ${minutes}m`;
|
|
26
|
+
}
|
package/dist/utils/types.d.ts
CHANGED
|
@@ -48,17 +48,36 @@ export interface BlueprintJob {
|
|
|
48
48
|
* @internal
|
|
49
49
|
*/
|
|
50
50
|
export interface BlueprintResource {
|
|
51
|
+
id: string;
|
|
52
|
+
displayName: string;
|
|
51
53
|
name: string;
|
|
52
54
|
kind: string;
|
|
53
55
|
src: string;
|
|
54
|
-
|
|
56
|
+
externalId: string;
|
|
55
57
|
}
|
|
56
58
|
/**
|
|
57
59
|
* @internal
|
|
58
60
|
*/
|
|
61
|
+
export interface BlueprintOperation {
|
|
62
|
+
id: string;
|
|
63
|
+
status: string;
|
|
64
|
+
createdAt?: string;
|
|
65
|
+
completedAt?: string;
|
|
66
|
+
}
|
|
59
67
|
export interface BlueprintStack {
|
|
68
|
+
id: string;
|
|
60
69
|
name: string;
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
displayName: string;
|
|
71
|
+
resources: Array<BlueprintResource>;
|
|
72
|
+
createdAt?: string;
|
|
73
|
+
updatedAt?: string;
|
|
74
|
+
recentOperation?: BlueprintOperation;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
export interface BlueprintLog {
|
|
80
|
+
timestamp: string;
|
|
81
|
+
message: string;
|
|
63
82
|
}
|
|
64
83
|
export {};
|
package/oclif.manifest.json
CHANGED
|
@@ -48,6 +48,50 @@
|
|
|
48
48
|
"info.js"
|
|
49
49
|
]
|
|
50
50
|
},
|
|
51
|
+
"blueprints:logs": {
|
|
52
|
+
"aliases": [],
|
|
53
|
+
"args": {},
|
|
54
|
+
"description": "Display logs for a Blueprint stack",
|
|
55
|
+
"examples": [
|
|
56
|
+
"<%= config.bin %> <%= command.id %>",
|
|
57
|
+
"<%= config.bin %> <%= command.id %> --stack-id <stack-id>",
|
|
58
|
+
"<%= config.bin %> <%= command.id %> --watch"
|
|
59
|
+
],
|
|
60
|
+
"flags": {
|
|
61
|
+
"stack-id": {
|
|
62
|
+
"char": "s",
|
|
63
|
+
"description": "Stack ID to fetch logs for (optional if running in a blueprint directory)",
|
|
64
|
+
"name": "stack-id",
|
|
65
|
+
"required": false,
|
|
66
|
+
"hasDynamicHelp": false,
|
|
67
|
+
"multiple": false,
|
|
68
|
+
"type": "option"
|
|
69
|
+
},
|
|
70
|
+
"watch": {
|
|
71
|
+
"char": "w",
|
|
72
|
+
"description": "Watch for new logs (streaming mode)",
|
|
73
|
+
"name": "watch",
|
|
74
|
+
"required": false,
|
|
75
|
+
"allowNo": false,
|
|
76
|
+
"type": "boolean"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"hasDynamicHelp": false,
|
|
80
|
+
"hiddenAliases": [],
|
|
81
|
+
"id": "blueprints:logs",
|
|
82
|
+
"pluginAlias": "@sanity/runtime-cli",
|
|
83
|
+
"pluginName": "@sanity/runtime-cli",
|
|
84
|
+
"pluginType": "core",
|
|
85
|
+
"strict": true,
|
|
86
|
+
"enableJsonFlag": false,
|
|
87
|
+
"isESM": true,
|
|
88
|
+
"relativePath": [
|
|
89
|
+
"dist",
|
|
90
|
+
"commands",
|
|
91
|
+
"blueprints",
|
|
92
|
+
"logs.js"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
51
95
|
"blueprints:plan": {
|
|
52
96
|
"aliases": [],
|
|
53
97
|
"args": {},
|
|
@@ -247,5 +291,5 @@
|
|
|
247
291
|
]
|
|
248
292
|
}
|
|
249
293
|
},
|
|
250
|
-
"version": "1.
|
|
294
|
+
"version": "1.3.1"
|
|
251
295
|
}
|