mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
eval: serialize returned objects as JSON
Objects and arrays returned from eval now serialize to JSON instead of "[object Object]". Native errors and functions retain their string form.
This commit is contained in:
@@ -56,6 +56,10 @@ pub fn isFunction(self: Value) bool {
|
||||
return v8.v8__Value__IsFunction(self.handle);
|
||||
}
|
||||
|
||||
pub fn isNativeError(self: Value) bool {
|
||||
return v8.v8__Value__IsNativeError(self.handle);
|
||||
}
|
||||
|
||||
pub fn isNull(self: Value) bool {
|
||||
return v8.v8__Value__IsNull(self.handle);
|
||||
}
|
||||
|
||||
@@ -1076,22 +1076,32 @@ fn runEval(arena: std.mem.Allocator, page: *lp.Frame, script: [:0]const u8, fall
|
||||
}
|
||||
|
||||
const settled = promise.result();
|
||||
const rejected = promise.state() == .rejected;
|
||||
// No-return async IIFE → undefined → silence, so pipes stay clean.
|
||||
if (promise.state() == .fulfilled and settled.isUndefined()) return .{ .text = "" };
|
||||
const text = settled.toStringSliceWithAlloc(arena) catch |err| switch (err) {
|
||||
if (!rejected and settled.isUndefined()) return .{ .text = "" };
|
||||
const text = (if (rejected) settled.toStringSliceWithAlloc(arena) else evalResultText(arena, settled)) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => return .{ .text = try formatJsError(arena, &try_catch, err), .is_error = true },
|
||||
};
|
||||
return .{ .text = text, .is_error = (promise.state() == .rejected) };
|
||||
return .{ .text = text, .is_error = rejected };
|
||||
}
|
||||
|
||||
const text = js_result.toStringSliceWithAlloc(arena) catch |err| switch (err) {
|
||||
const text = evalResultText(arena, js_result) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => return .{ .text = try formatJsError(arena, &try_catch, err), .is_error = true },
|
||||
};
|
||||
return .{ .text = text };
|
||||
}
|
||||
|
||||
/// Objects/arrays serialize as JSON so `return obj` prints data, not
|
||||
/// `[object Object]`; errors and primitives keep their string form.
|
||||
fn evalResultText(arena: std.mem.Allocator, value: lp.js.Value) ![]u8 {
|
||||
if (value.isObject() and !value.isFunction() and !value.isNativeError()) {
|
||||
return value.toJson(arena);
|
||||
}
|
||||
return value.toStringSliceWithAlloc(arena);
|
||||
}
|
||||
|
||||
fn formatJsError(arena: std.mem.Allocator, try_catch: *lp.js.TryCatch, err: anyerror) error{OutOfMemory}![]const u8 {
|
||||
const caught = try_catch.caughtOrError(arena, err);
|
||||
var aw: std.Io.Writer.Allocating = .init(arena);
|
||||
|
||||
@@ -587,6 +587,30 @@ test "MCP - eval: bare expression still returns its value" {
|
||||
} }, out.written());
|
||||
}
|
||||
|
||||
test "MCP - eval: object return serializes as JSON" {
|
||||
defer testing.reset();
|
||||
var out: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
|
||||
const msg =
|
||||
\\{
|
||||
\\ "jsonrpc": "2.0",
|
||||
\\ "id": 1,
|
||||
\\ "method": "tools/call",
|
||||
\\ "params": {
|
||||
\\ "name": "eval",
|
||||
\\ "arguments": { "script": "return { n: 42, items: [1, 2] };" }
|
||||
\\ }
|
||||
\\}
|
||||
;
|
||||
try router.handleMessage(server, testing.arena_allocator, msg);
|
||||
|
||||
try testing.expectJson(.{ .id = 1, .result = .{
|
||||
.content = &.{.{ .type = "text", .text = "{\"n\":42,\"items\":[1,2]}" }},
|
||||
} }, out.written());
|
||||
}
|
||||
|
||||
test "MCP - eval: localStorage persists across navigations and is origin-scoped" {
|
||||
defer testing.reset();
|
||||
var out: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
|
||||
Reference in New Issue
Block a user