Skip to content

Commit 680c38f

Browse files
mxvshclaude
andcommitted
feat: display console output inline with directive status
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 3dacda1 commit 680c38f

File tree

6 files changed

+100
-2
lines changed

6 files changed

+100
-2
lines changed

packages/engine/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@restflow/http": "workspace:*",
2727
"@restflow/assertions": "workspace:*",
2828
"@restflow/reporter": "workspace:*",
29+
"picocolors": "^1.1.1",
2930
"tslib": "^2.3.0"
3031
}
3132
}

packages/engine/src/core/engine.ts

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import {
55
import { EnvironmentManager } from "@restflow/environment";
66
import { HttpClient } from "@restflow/http";
77
import { parseFlow } from "@restflow/parser";
8+
import pc from "picocolors";
89
import type {
910
AssertDirective,
1011
CaptureDirective,
12+
ConsoleDirective,
1113
DirectiveResult,
1214
ExecutionContext,
1315
Flow,
@@ -239,7 +241,7 @@ export class FlowExecutor {
239241
* Evaluate all directives for a response
240242
*/
241243
private evaluateDirectives(
242-
directives: Array<CaptureDirective | AssertDirective>,
244+
directives: Array<CaptureDirective | AssertDirective | ConsoleDirective>,
243245
response: HttpResponse,
244246
context: ExecutionContext,
245247
): DirectiveResult[] {
@@ -249,6 +251,8 @@ export class FlowExecutor {
249251
return this.evaluateCaptureDirective(directive, response);
250252
} else if (directive.type === "assert") {
251253
return this.evaluateAssertDirective(directive, response, context);
254+
} else if (directive.type === "console") {
255+
return this.evaluateConsoleDirective(directive, response, context);
252256
} else {
253257
return {
254258
directive,
@@ -327,6 +331,75 @@ export class FlowExecutor {
327331
}
328332
}
329333

334+
/**
335+
* Evaluate a console directive
336+
*/
337+
private evaluateConsoleDirective(
338+
directive: ConsoleDirective,
339+
response: HttpResponse,
340+
context: ExecutionContext,
341+
): DirectiveResult {
342+
try {
343+
const value = this.valueExtractor.extract(
344+
directive.expression,
345+
response,
346+
);
347+
348+
// Generate formatted console output without printing
349+
const consoleOutput = this.formatConsoleValue(directive.expression, value);
350+
351+
return {
352+
directive,
353+
success: true,
354+
consoleOutput,
355+
};
356+
} catch (error) {
357+
return {
358+
directive,
359+
success: false,
360+
error: `Failed to console log value: ${error instanceof Error ? error.message : String(error)}`,
361+
};
362+
}
363+
}
364+
365+
/**
366+
* Format a value for console output with colors and formatting
367+
*/
368+
private formatConsoleValue(expression: string, value: unknown): string {
369+
const lines: string[] = [];
370+
371+
if (value === null) {
372+
lines.push(pc.gray('null'));
373+
} else if (value === undefined) {
374+
lines.push(pc.gray('undefined'));
375+
} else if (typeof value === 'string') {
376+
lines.push(pc.green(`"${value}"`));
377+
} else if (typeof value === 'number') {
378+
lines.push(pc.yellow(String(value)));
379+
} else if (typeof value === 'boolean') {
380+
lines.push(pc.magenta(String(value)));
381+
} else if (typeof value === 'object') {
382+
try {
383+
const formatted = JSON.stringify(value, null, 2);
384+
// Basic JSON syntax highlighting
385+
const highlighted = formatted
386+
.replace(/"([^"]+)":/g, `${pc.cyan('"$1"')}:`) // Keys
387+
.replace(/: "([^"]+)"/g, `: ${pc.green('"$1"')}`) // String values
388+
.replace(/: (\d+)/g, `: ${pc.yellow('$1')}`) // Numbers
389+
.replace(/: (true|false)/g, `: ${pc.magenta('$1')}`) // Booleans
390+
.replace(/: null/g, `: ${pc.gray('null')}`); // Null
391+
392+
lines.push(...highlighted.split('\n'));
393+
} catch {
394+
lines.push(pc.white(String(value)));
395+
}
396+
} else {
397+
lines.push(pc.white(String(value)));
398+
}
399+
400+
return lines.join('\n');
401+
}
402+
330403
/**
331404
* Process capture directives to update execution context variables
332405
*/

packages/parser/src/parsers/parser.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {
22
AssertDirective,
33
CaptureDirective,
4+
ConsoleDirective,
45
Directive,
56
Flow,
67
FlowStep,
@@ -160,6 +161,12 @@ function parseDirectives(lines: string[]): Directive[] {
160161
type: "assert",
161162
expression,
162163
} as AssertDirective);
164+
} else if (directiveContent.startsWith("console ")) {
165+
const expression = directiveContent.substring("console ".length);
166+
directives.push({
167+
type: "console",
168+
expression,
169+
} as ConsoleDirective);
163170
}
164171
}
165172

packages/reporter/src/reporters/console-reporter.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ export class ConsoleReporter implements Reporter {
9292
const description = this.getDirectiveDescription(directive.directive);
9393

9494
console.log(` ${statusColored} ${type}: ${description}`);
95+
96+
// Show console output if it's a console directive
97+
if (directive.directive.type === "console" && directive.consoleOutput) {
98+
const consoleLines = directive.consoleOutput.split('\n');
99+
consoleLines.forEach(line => {
100+
console.log(` ${line}`);
101+
});
102+
}
95103
});
96104

97105
// Show headers if requested
@@ -282,6 +290,11 @@ export class ConsoleReporter implements Reporter {
282290
? pc.dim(directive.expression)
283291
: directive.expression;
284292
return `${variable}${expression}`;
293+
} else if (directive.type === "console") {
294+
const expression = this.options.colors
295+
? pc.dim(directive.expression)
296+
: directive.expression;
297+
return `${expression}`;
285298
}
286299
return "unknown";
287300
}

packages/types/src/directives.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ export interface DirectiveResult {
2424
success: boolean;
2525
error?: string;
2626
capturedValue?: unknown;
27+
consoleOutput?: string;
2728
}

pnpm-lock.yaml

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)