Improve the error message when Bun.serve() is passed a non-Response object (#11562)

Co-authored-by: dave caruso <me@paperdave.net>
This commit is contained in:
Jarred Sumner
2024-06-05 20:35:47 -07:00
committed by GitHub
parent 6a756bf979
commit 6fd47d6846
3 changed files with 43 additions and 18 deletions

View File

@@ -1403,16 +1403,42 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
return JSValue.jsUndefined();
}
fn renderMissingInvalidResponse(ctx: *RequestContext, value: JSC.JSValue) void {
var class_name = value.getClassInfoName() orelse bun.String.empty;
defer class_name.deref();
const globalThis: *JSC.JSGlobalObject = ctx.server.globalThis;
Output.enableBuffering();
var writer = Output.errorWriter();
if (class_name.eqlComptime("Response")) {
Output.errGeneric("Expected a native Response object, but received a polyfilled Response object. Bun.serve() only supports native Response objects.", .{});
} else if (!value.isEmpty() and !globalThis.hasException()) {
var formatter = JSC.ConsoleObject.Formatter{
.globalThis = globalThis,
.quote_strings = true,
};
Output.errGeneric("Expected a Response object, but received '{}'", .{value.toFmt(formatter.globalThis, &formatter)});
} else {
Output.errGeneric("Expected a Response object", .{});
}
Output.flush();
if (!globalThis.hasException()) {
JSC.ConsoleObject.writeTrace(@TypeOf(&writer), &writer, globalThis);
}
Output.flush();
ctx.renderMissing();
}
fn handleResolve(ctx: *RequestContext, value: JSC.JSValue) void {
if (value.isEmptyOrUndefinedOrNull() or !value.isCell()) {
ctx.renderMissing();
ctx.renderMissingInvalidResponse(value);
return;
}
const response = value.as(JSC.WebCore.Response) orelse {
Output.prettyErrorln("Expected a Response object", .{});
Output.flush();
ctx.renderMissing();
ctx.renderMissingInvalidResponse(value);
return;
};
ctx.response_jsvalue = value;
@@ -2558,7 +2584,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
}
if (response_value.isEmptyOrUndefinedOrNull()) {
ctx.renderMissing();
ctx.renderMissingInvalidResponse(response_value);
return;
}
@@ -2611,11 +2637,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
}
if (fulfilled_value.isEmptyOrUndefinedOrNull()) {
ctx.renderMissing();
ctx.renderMissingInvalidResponse(fulfilled_value);
return;
}
var response = fulfilled_value.as(JSC.WebCore.Response) orelse {
ctx.renderMissing();
ctx.renderMissingInvalidResponse(fulfilled_value);
return;
};

View File

@@ -5600,16 +5600,12 @@ pub const JSValue = enum(JSValueReprInt) {
extern fn JSC__JSValue__getClassInfoName(value: JSValue, out: *bun.String) bool;
/// Returned memory is read-only memory of the s_info assigned to a JSCell
pub fn getClassInfoName(this: JSValue) ?[]const u8 {
var out: bun.String = undefined;
/// For native C++ classes extending JSCell, this retrieves s_info's name
pub fn getClassInfoName(this: JSValue) ?bun.String {
if (!this.isObject()) return null;
var out: bun.String = bun.String.empty;
if (!JSC__JSValue__getClassInfoName(this, &out)) return null;
// we assume the class name is ASCII text
const data = out.latin1();
if (bun.Environment.allow_assert) {
bun.assert(bun.strings.isAllASCII(data));
}
return data;
return out;
}
};

View File

@@ -7422,12 +7422,15 @@ pub const Macro = struct {
.String => this.coerce(value, .String),
.Promise => this.coerce(value, .Promise),
else => brk: {
var name = value.getClassInfoName() orelse bun.String.init("unknown");
defer name.deref();
this.log.addErrorFmt(
this.source,
this.caller.loc,
this.allocator,
"cannot coerce {s} ({s}) to Bun's AST. Please return a simpler type",
.{ value.getClassInfoName() orelse "unknown", @tagName(value.jsType()) },
"cannot coerce {} ({s}) to Bun's AST. Please return a simpler type",
.{ name, @tagName(value.jsType()) },
) catch unreachable;
break :brk error.MacroFailed;
},