Merge branch 'zack/ssg-3' of github.com:oven-sh/bun into ali/react

This commit is contained in:
Alistair Smith
2025-09-23 14:22:31 -07:00
8 changed files with 256 additions and 160 deletions

View File

@@ -1098,10 +1098,10 @@ const ReqOrSaved = union(enum) {
};
}
pub fn url(this: *const @This(), alloc: Allocator) []const u8 {
pub fn url(this: *const @This(), alloc: Allocator) bun.ZigString.Slice {
return switch (this.*) {
.req => |req| req.url(),
.saved => |saved| saved.request.url.toUTF8(alloc).slice(),
.req => |req| bun.ZigString.Slice.fromUTF8NeverFree(req.url()),
.saved => |saved| saved.request.url.toUTF8(alloc),
};
}
};
@@ -1120,7 +1120,6 @@ fn deferRequest(
deferred.data = .{
.route_bundle_index = route_bundle_index,
.dev = dev,
.ref_count = .init(),
.handler = switch (kind) {
.bundled_html_page => brk: {
resp.onAborted(*DeferredRequest, DeferredRequest.onAbort, &deferred.data);
@@ -1134,14 +1133,28 @@ fn deferRequest(
},
.saved => |saved| saved,
};
server_handler.ctx.setAdditionalOnAbortCallback(.{ .cb = DeferredRequest.onAbortWrapper, .data = &deferred.data });
server_handler.ctx.ref();
server_handler.ctx.setAdditionalOnAbortCallback(.{
.cb = DeferredRequest.onAbortWrapper,
.data = &deferred.data,
.deref_fn = struct {
fn deref_fn(ptr: *anyopaque) void {
var self: *DeferredRequest = @ptrCast(@alignCast(ptr));
self.weakDeref();
}
}.deref_fn,
});
break :brk .{
.server_handler = server_handler,
};
},
},
};
deferred.data.ref();
if (deferred.data.handler == .server_handler) {
deferred.data.weakRef();
}
requests_array.prepend(deferred);
}
@@ -1589,24 +1602,45 @@ pub const DeferredRequest = struct {
pub const List = std.SinglyLinkedList(DeferredRequest);
pub const Node = List.Node;
const RefCount = bun.ptr.RefCount(@This(), "ref_count", deinitImpl, .{
.debug_name = "DeferredRequest",
});
const debugLog = bun.Output.Scoped("DlogeferredRequest", .hidden).log;
route_bundle_index: RouteBundle.Index,
handler: Handler,
dev: *DevServer,
/// This struct can have at most 2 references it:
/// - The dev server (`dev.current_bundle.requests`)
/// - uws.Response as a user data pointer
ref_count: RefCount,
/// This struct can be referenced by the dev server (`dev.current_bundle.requests`)
///
/// Simultaneously, RequestContext may refer to it for AdditionalOnAbortCallback,
/// but we treat this as a weak reference, otherwise we will have reference
/// count cycles as this struct itself may store a reference to
/// RequestContext in the `server_handler` variant of `Handler`
referenced_by_devserver: bool = true,
weakly_referenced_by_requestcontext: bool = false,
// expose `ref` and `deref` as public methods
pub const ref = RefCount.ref;
pub const deref = RefCount.deref;
pub fn isAlive(this: *DeferredRequest) bool {
return this.referenced_by_devserver;
}
pub fn deref(this: *DeferredRequest) void {
this.referenced_by_devserver = false;
const should_free = !this.weakly_referenced_by_requestcontext;
this.__deinit();
if (should_free) {
this.__free();
}
}
pub fn weakRef(this: *DeferredRequest) void {
bun.assert(!this.weakly_referenced_by_requestcontext);
this.weakly_referenced_by_requestcontext = true;
}
pub fn weakDeref(this: *DeferredRequest) void {
this.weakly_referenced_by_requestcontext = false;
if (!this.referenced_by_devserver) {
this.__free();
}
}
const Handler = union(enum) {
/// For a .framework route. This says to call and render the page.
@@ -1628,6 +1662,7 @@ pub const DeferredRequest = struct {
fn onAbortWrapper(this: *anyopaque) void {
const self: *DeferredRequest = @alignCast(@ptrCast(this));
if (!self.isAlive()) return;
self.onAbortImpl();
}
@@ -1642,16 +1677,19 @@ pub const DeferredRequest = struct {
assert(this.handler == .aborted);
}
/// Actually free the underlying allocation for the node, does not deinitialize children
fn __free(this: *DeferredRequest) void {
this.dev.deferred_request_pool.put(@fieldParentPtr("data", this));
}
/// *WARNING*: Do not call this directly, instead call `.deref()`
///
/// Calling this is only required if the desired handler is going to be avoided,
/// such as for bundling failures or aborting the server.
/// Does not free the underlying `DeferredRequest.Node`
fn deinitImpl(this: *DeferredRequest) void {
fn __deinit(this: *DeferredRequest) void {
debugLog("DeferredRequest(0x{x}) deinitImpl", .{@intFromPtr(this)});
this.ref_count.assertNoRefs();
defer this.dev.deferred_request_pool.put(@fieldParentPtr("data", this));
switch (this.handler) {
.server_handler => |*saved| saved.deinit(),
.bundled_html_page, .aborted => {},

View File

@@ -465,6 +465,7 @@ pub const Run = struct {
}
bun.api.napi.fixDeadCodeElimination();
bun.webcore.BakeResponse.fixDeadCodeElimination();
bun.crash_handler.fixDeadCodeElimination();
@import("./bun.js/bindings/JSSecrets.zig").fixDeadCodeElimination();
vm.globalExit();

View File

@@ -243,6 +243,28 @@ pub fn onAbort(self: AnyRequestContext, response: uws.AnyResponse) void {
}
}
pub fn ref(self: AnyRequestContext) void {
if (self.tagged_pointer.isNull()) {
return;
}
switch (self.tagged_pointer.tag()) {
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
self.tagged_pointer.as(HTTPServer.RequestContext).ref();
},
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
self.tagged_pointer.as(HTTPSServer.RequestContext).ref();
},
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
self.tagged_pointer.as(DebugHTTPServer.RequestContext).ref();
},
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
self.tagged_pointer.as(DebugHTTPSServer.RequestContext).ref();
},
else => @panic("Unexpected AnyRequestContext tag"),
}
}
pub fn deref(self: AnyRequestContext) void {
if (self.tagged_pointer.isNull()) {
return;

View File

@@ -11,6 +11,11 @@
pub const AdditionalOnAbortCallback = struct {
cb: *const fn (this: *anyopaque) void,
data: *anyopaque,
deref_fn: *const fn (this: *anyopaque) void,
pub fn deref(this: AdditionalOnAbortCallback) void {
this.deref_fn(this.data);
}
};
pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comptime ThisServer: type) type {
@@ -258,6 +263,11 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool,
this.request_body = null;
}
if (this.additional_on_abort) |cb| {
cb.deref();
this.additional_on_abort = null;
}
if (this.server) |server| {
this.server = null;
server.request_pool_allocator.put(this);
@@ -266,7 +276,7 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool,
}
pub fn deref(this: *RequestContext) void {
streamLog("deref", .{});
streamLog("deref {d} -> {d}", .{ this.ref_count, this.ref_count - 1 });
assert(this.ref_count > 0);
const ref_count = this.ref_count;
this.ref_count -= 1;
@@ -277,7 +287,7 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool,
}
pub fn ref(this: *RequestContext) void {
streamLog("ref", .{});
streamLog("ref {d} -> {d}", .{ this.ref_count, this.ref_count + 1 });
this.ref_count += 1;
}
@@ -599,6 +609,7 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool,
// mark request as aborted
this.flags.aborted = true;
if (this.additional_on_abort) |abort| {
defer abort.deref();
this.additional_on_abort = null;
abort.cb(abort.data);
}

View File

@@ -33,10 +33,10 @@ static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugInfo);
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugStack);
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugTask);
extern JSC_CALLCONV void* JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructForSSR(JSC::JSGlobalObject*, JSC::CallFrame*, int*);
extern JSC_CALLCONV void* JSC_HOST_CALL_ATTRIBUTES BakeResponseClass__constructForSSR(JSC::JSGlobalObject*, JSC::CallFrame*, int*);
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructError(JSC::JSGlobalObject*, JSC::CallFrame*);
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructJSON(JSC::JSGlobalObject*, JSC::CallFrame*);
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructRender(JSC::JSGlobalObject*, JSC::CallFrame*);
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES BakeResponseClass__constructRender(JSC::JSGlobalObject*, JSC::CallFrame*);
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructRedirect(JSC::JSGlobalObject*, JSC::CallFrame*);
extern JSC_CALLCONV size_t Response__estimatedSize(void* ptr);
@@ -76,7 +76,7 @@ extern "C" bool JSC__JSValue__isJSXElement(JSC::EncodedJSValue JSValue0, JSC::JS
return isJSXElement(JSValue0, globalObject);
}
extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES Response__createForSSR(Zig::GlobalObject* globalObject, void* ptr, uint8_t kind)
extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES BakeResponse__createForSSR(Zig::GlobalObject* globalObject, void* ptr, uint8_t kind)
{
Structure* structure = globalObject->bakeAdditions().JSBakeResponseStructure(globalObject);
@@ -102,7 +102,7 @@ static const HashTableValue JSBakeResponseConstructorTableValues[] = {
{ "json"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponseClass__constructJSON, 0 } },
{ "redirect"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponseClass__constructRedirect, 0 } },
{ "render"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponseClass__constructRender, 0 } }
{ "render"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, BakeResponseClass__constructRender, 0 } }
};
@@ -213,7 +213,7 @@ public:
JSBakeResponse* instance = JSBakeResponse::create(vm, globalObject, structure, nullptr);
int arg_was_jsx = 0;
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, &arg_was_jsx);
void* ptr = BakeResponseClass__constructForSSR(globalObject, callFrame, &arg_was_jsx);
if (scope.exception()) [[unlikely]] {
ASSERT_WITH_MESSAGE(!ptr, "Memory leak detected: new Response() allocated memory without checking for exceptions.");
return JSValue::encode(JSC::jsUndefined());
@@ -243,7 +243,7 @@ public:
Structure* structure = globalObject->bakeAdditions().JSBakeResponseStructure(globalObject);
JSBakeResponse* instance = JSBakeResponse::create(vm, globalObject, structure, nullptr);
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, nullptr);
void* ptr = BakeResponseClass__constructForSSR(globalObject, callFrame, nullptr);
if (scope.exception()) [[unlikely]] {
ASSERT_WITH_MESSAGE(!ptr, "Memory leak detected: new Response() allocated memory without checking for exceptions.");
return JSValue::encode(JSC::jsUndefined());

View File

@@ -19,6 +19,7 @@ pub const AutoFlusher = @import("./webcore/AutoFlusher.zig");
pub const EncodingLabel = @import("./webcore/EncodingLabel.zig").EncodingLabel;
pub const Fetch = @import("./webcore/fetch.zig");
pub const Response = @import("./webcore/Response.zig");
pub const BakeResponse = @import("./webcore/BakeResponse.zig");
pub const TextDecoder = @import("./webcore/TextDecoder.zig");
pub const TextEncoder = @import("./webcore/TextEncoder.zig");
pub const TextEncoderStreamEncoder = @import("./webcore/TextEncoderStreamEncoder.zig");

View File

@@ -0,0 +1,143 @@
pub fn fixDeadCodeElimination() void {
std.mem.doNotOptimizeAway(&BakeResponseClass__constructForSSR);
std.mem.doNotOptimizeAway(&BakeResponseClass__constructRender);
}
extern "C" fn BakeResponse__createForSSR(globalObject: *JSGlobalObject, this: *Response, kind: u8) callconv(jsc.conv) jsc.JSValue;
/// Corresponds to `JSBakeResponseKind` in
/// `src/bun.js/bindings/JSBakeResponse.h`
const SSRKind = enum(u8) {
regular = 0,
redirect = 1,
render = 2,
};
pub fn toJSForSSR(this: *Response, globalObject: *JSGlobalObject, kind: SSRKind) JSValue {
this.calculateEstimatedByteSize();
return BakeResponse__createForSSR(globalObject, this, @intFromEnum(kind));
}
pub export fn BakeResponseClass__constructForSSR(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame, bake_ssr_has_jsx: ?*c_int) callconv(jsc.conv) ?*anyopaque {
return @as(*Response, constructor(globalObject, callFrame, bake_ssr_has_jsx) catch |err| switch (err) {
error.JSError => return null,
error.OutOfMemory => {
globalObject.throwOutOfMemory() catch {};
return null;
},
});
}
pub fn constructor(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, bake_ssr_has_jsx: ?*c_int) bun.JSError!*Response {
var arguments = callframe.argumentsAsArray(2);
// Allow `return new Response(<jsx> ... </jsx>, { ... }`
// inside of a react component
if (!arguments[0].isUndefinedOrNull() and arguments[0].isObject()) {
bake_ssr_has_jsx.?.* = 0;
if (try arguments[0].isJSXElement(globalThis)) {
const vm = globalThis.bunVM();
if (vm.getDevServerAsyncLocalStorage()) |async_local_storage| {
try assertStreamingDisabled(globalThis, async_local_storage, "new Response(<jsx />, { ... })");
}
bake_ssr_has_jsx.?.* = 1;
}
}
return Response.constructor(globalThis, callframe);
}
pub fn constructRedirect(
globalThis: *jsc.JSGlobalObject,
callframe: *jsc.CallFrame,
) bun.JSError!JSValue {
const response = try Response.constructRedirectImpl(globalThis, callframe);
const ptr = bun.new(Response, response);
const vm = globalThis.bunVM();
// Check if dev_server_async_local_storage is set (indicating we're in Bun dev server)
if (vm.getDevServerAsyncLocalStorage()) |async_local_storage| {
try assertStreamingDisabled(globalThis, async_local_storage, "Response.redirect");
return toJSForSSR(ptr, globalThis, .redirect);
}
return ptr.toJS(globalThis);
}
pub export fn BakeResponseClass__constructRender(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame) callconv(jsc.conv) jsc.JSValue {
return @call(.always_inline, jsc.toJSHostFn(constructRender), .{ globalObject, callFrame });
}
/// This function is only available on JSBakeResponse
pub fn constructRender(
globalThis: *jsc.JSGlobalObject,
callframe: *jsc.CallFrame,
) bun.JSError!JSValue {
const arguments = callframe.argumentsAsArray(2);
const vm = globalThis.bunVM();
// Check if dev server async local_storage is set
const async_local_storage = vm.getDevServerAsyncLocalStorage() orelse {
return globalThis.throwInvalidArguments("Response.render() is only available in the Bun dev server", .{});
};
try assertStreamingDisabled(globalThis, async_local_storage, "Response.render");
// Validate arguments
if (arguments.len < 1) {
return globalThis.throwInvalidArguments("Response.render() requires at least a path argument", .{});
}
const path_arg = arguments[0];
if (!path_arg.isString()) {
return globalThis.throwInvalidArguments("Response.render() path must be a string", .{});
}
// Get the path string
const path_str = try path_arg.toSlice(globalThis, bun.default_allocator);
// Duplicate the path string so it persists
const path_copy = bun.default_allocator.dupe(u8, path_str.slice()) catch {
path_str.deinit();
return globalThis.throwOutOfMemory();
};
path_str.deinit();
// Create a Response with Render body
const response = bun.new(Response, Response{
.body = Body{
.value = .{
.Render = .{
.path = bun.ptr.Owned([]const u8).fromRaw(path_copy),
},
},
},
.init = Response.Init{
.status_code = 200,
},
});
const response_js = toJSForSSR(response, globalThis, .render);
response_js.ensureStillAlive();
return response_js;
}
fn assertStreamingDisabled(globalThis: *jsc.JSGlobalObject, async_local_storage: JSValue, display_function: []const u8) bun.JSError!void {
if (async_local_storage.isEmptyOrUndefinedOrNull() or !async_local_storage.isObject()) return globalThis.throwInvalidArguments("store value must be an object", .{});
const getStoreFn = (try async_local_storage.getPropertyValue(globalThis, "getStore")) orelse return globalThis.throwInvalidArguments("store value must have a \"getStore\" field", .{});
const store_value = try getStoreFn.call(globalThis, async_local_storage, &.{});
const streaming_val = (try store_value.getPropertyValue(globalThis, "streaming")) orelse return globalThis.throwInvalidArguments("store value must have a \"streaming\" field", .{});
if (!streaming_val.isBoolean()) return globalThis.throwInvalidArguments("\"streaming\" fied must be a boolean", .{});
if (streaming_val.asBoolean()) return globalThis.throwInvalidArguments("\"{s}\" is not available when `export const streaming = true`", .{display_function});
}
const bun = @import("bun");
const std = @import("std");
const jsc = bun.jsc;
const JSGlobalObject = jsc.JSGlobalObject;
const JSValue = jsc.JSValue;
const Body = bun.webcore.Body;
const Response = bun.webcore.Response;

View File

@@ -66,21 +66,6 @@ pub fn toJS(this: *Response, globalObject: *JSGlobalObject) JSValue {
return js.toJSUnchecked(globalObject, this);
}
/// Corresponds to `JSBakeResponseKind` in
/// `src/bun.js/bindings/JSBakeResponse.h`
const SSRKind = enum(u8) {
regular = 0,
redirect = 1,
render = 2,
};
extern "C" fn Response__createForSSR(globalObject: *JSGlobalObject, this: *Response, kind: u8) callconv(jsc.conv) jsc.JSValue;
pub fn toJSForSSR(this: *Response, globalObject: *JSGlobalObject, kind: SSRKind) JSValue {
this.calculateEstimatedByteSize();
return Response__createForSSR(globalObject, this, @intFromEnum(kind));
}
pub fn getBodyValue(
this: *Response,
) *Body.Value {
@@ -334,7 +319,6 @@ pub fn cloneValue(
}
pub fn clone(this: *Response, globalThis: *JSGlobalObject) bun.JSError!*Response {
// TODO: handle clone for jsxElement for bake?
return bun.new(Response, try this.cloneValue(globalThis));
}
@@ -473,6 +457,16 @@ pub fn constructRedirect(
globalThis: *jsc.JSGlobalObject,
callframe: *jsc.CallFrame,
) bun.JSError!JSValue {
const response = try constructRedirectImpl(globalThis, callframe);
const ptr = bun.new(Response, response);
const response_js = ptr.toJS(globalThis);
return response_js;
}
pub fn constructRedirectImpl(
globalThis: *jsc.JSGlobalObject,
callframe: *jsc.CallFrame,
) bun.JSError!Response {
var args_list = callframe.arguments_old(4);
// https://github.com/remix-run/remix/blob/db2c31f64affb2095e4286b91306b96435967969/packages/remix-server-runtime/responses.ts#L4
var args = jsc.CallFrame.ArgumentsSlice.init(globalThis.bunVM(), args_list.ptr[0..args_list.len]);
@@ -519,7 +513,7 @@ pub fn constructRedirect(
}
if (globalThis.hasException()) {
return .zero;
return error.JSError;
}
did_succeed = true;
break :brk response;
@@ -528,77 +522,7 @@ pub fn constructRedirect(
response.init.headers = try response.getOrCreateHeaders(globalThis);
var headers_ref = response.init.headers.?;
try headers_ref.put(.Location, url_string_slice.slice(), globalThis);
const ptr = bun.new(Response, response);
const vm = globalThis.bunVM();
// Check if dev_server_async_local_storage is set (indicating we're in Bun dev server)
if (vm.getDevServerAsyncLocalStorage()) |async_local_storage| {
try assertStreamingDisabled(globalThis, async_local_storage, "Response.redirect");
return ptr.toJSForSSR(globalThis, .redirect);
}
const response_js = ptr.toJS(globalThis);
return response_js;
}
pub export fn ResponseClass__constructRender(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame) callconv(jsc.conv) jsc.JSValue {
return @call(.always_inline, jsc.toJSHostFn(constructRender), .{ globalObject, callFrame });
}
/// This function is only available on JSBakeResponse
pub fn constructRender(
globalThis: *jsc.JSGlobalObject,
callframe: *jsc.CallFrame,
) bun.JSError!JSValue {
const arguments = callframe.argumentsAsArray(2);
const vm = globalThis.bunVM();
// Check if dev server async local_storage is set
const async_local_storage = vm.getDevServerAsyncLocalStorage() orelse {
return globalThis.throwInvalidArguments("Response.render() is only available in the Bun dev server", .{});
};
try assertStreamingDisabled(globalThis, async_local_storage, "Response.render");
// Validate arguments
if (arguments.len < 1) {
return globalThis.throwInvalidArguments("Response.render() requires at least a path argument", .{});
}
const path_arg = arguments[0];
if (!path_arg.isString()) {
return globalThis.throwInvalidArguments("Response.render() path must be a string", .{});
}
// Get the path string
const path_str = try path_arg.toSlice(globalThis, bun.default_allocator);
// Duplicate the path string so it persists
const path_copy = bun.default_allocator.dupe(u8, path_str.slice()) catch {
path_str.deinit();
return globalThis.throwOutOfMemory();
};
path_str.deinit();
// Create a Response with Render body
var response = bun.new(Response, Response{
.body = Body{
.value = .{
.Render = .{
.path = bun.ptr.Owned([]const u8).fromRaw(path_copy),
},
},
},
.init = Response.Init{
.status_code = 200,
},
});
const response_js = response.toJSForSSR(globalThis, .render);
response_js.ensureStillAlive();
return response_js;
return response;
}
pub fn constructError(
@@ -621,28 +545,6 @@ pub fn constructError(
}
pub fn constructor(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!*Response {
return constructorImpl(globalThis, callframe, null);
}
pub fn ResponseClass__constructForSSR(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame, bake_ssr_has_jsx: ?*c_int) callconv(jsc.conv) ?*anyopaque {
return @as(*Response, Response.constructorForSSR(globalObject, callFrame, bake_ssr_has_jsx) catch |err| switch (err) {
error.JSError => return null,
error.OutOfMemory => {
globalObject.throwOutOfMemory() catch {};
return null;
},
});
}
comptime {
@export(&ResponseClass__constructForSSR, .{ .name = "ResponseClass__constructForSSR" });
}
pub fn constructorForSSR(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, bake_ssr_has_jsx: ?*c_int) bun.JSError!*Response {
return constructorImpl(globalThis, callframe, bake_ssr_has_jsx);
}
pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, bake_ssr_has_jsx: ?*c_int) bun.JSError!*Response {
var arguments = callframe.argumentsAsArray(2);
if (!arguments[0].isUndefinedOrNull() and arguments[0].isObject()) {
@@ -677,19 +579,6 @@ pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFram
return bun.new(Response, response);
}
}
// Special case for bake: allow `return new Response(<jsx> ... </jsx>, { ... }`
// inside of a react component
if (bake_ssr_has_jsx != null) {
bake_ssr_has_jsx.?.* = 0;
if (try arguments[0].isJSXElement(globalThis)) {
const vm = globalThis.bunVM();
if (vm.getDevServerAsyncLocalStorage()) |async_local_storage| {
try assertStreamingDisabled(globalThis, async_local_storage, "new Response(<jsx />, { ... })");
}
bake_ssr_has_jsx.?.* = 1;
}
}
}
var init: Init = (brk: {
if (arguments[1].isUndefinedOrNull()) {
@@ -872,15 +761,6 @@ inline fn emptyWithStatus(_: *jsc.JSGlobalObject, status: u16) Response {
});
}
fn assertStreamingDisabled(globalThis: *jsc.JSGlobalObject, async_local_storage: JSValue, display_function: []const u8) bun.JSError!void {
if (async_local_storage.isEmptyOrUndefinedOrNull() or !async_local_storage.isObject()) return globalThis.throwInvalidArguments("store value must be an object", .{});
const getStoreFn = (try async_local_storage.getPropertyValue(globalThis, "getStore")) orelse return globalThis.throwInvalidArguments("store value must have a \"getStore\" field", .{});
const store_value = try getStoreFn.call(globalThis, async_local_storage, &.{});
const streaming_val = (try store_value.getPropertyValue(globalThis, "streaming")) orelse return globalThis.throwInvalidArguments("store value must have a \"streaming\" field", .{});
if (!streaming_val.isBoolean()) return globalThis.throwInvalidArguments("\"streaming\" fied must be a boolean", .{});
if (streaming_val.asBoolean()) return globalThis.throwInvalidArguments("\"{s}\" is not available when `export const streaming = true`", .{display_function});
}
/// https://developer.mozilla.org/en-US/docs/Web/API/Headers
// TODO: move to http.zig. this has nothing to do with jsc or WebCore