okie dokie

This commit is contained in:
Zack Radisic
2025-09-05 15:58:38 -07:00
parent 862f7378e4
commit eeecbfa790
9 changed files with 230 additions and 527 deletions

View File

@@ -93,6 +93,11 @@ pub fn VisitExpr(
}
// Transform Response -> BakeResponse for server-side code
// TODO: this does the incorrect thing in this case:
// ```
// const Response = 'ooga booga!'
// console.log(Response)
// ```
if (p.options.features.server_components.isServerSide() and
bun.strings.eqlComptime(name, "Response"))
{
@@ -101,6 +106,8 @@ pub fn VisitExpr(
e_.ref = bake_response_result.ref;
e_.must_keep_due_to_with_stmt = bake_response_result.is_inside_with_scope;
std.debug.print("{s}: Response -> SSRResponse\n", .{p.source.path.pretty});
// Handle the rest of the identifier processing with the new ref
return p.handleIdentifier(expr.loc, e_, "SSRResponse", IdentifierOpts{
.assign_target = in.assign_target,

View File

@@ -23,9 +23,6 @@
namespace Bun {
static JSC_DECLARE_HOST_FUNCTION(jsBakeResponseConstructorRender);
static JSC_DECLARE_HOST_FUNCTION(jsBakeResponseConstructorRedirect);
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetSymbolFor);
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetType);
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetKey);
@@ -36,36 +33,114 @@ 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*, JSC::EncodedJSValue);
extern JSC_CALLCONV void* JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructForSSR(JSC::JSGlobalObject*, JSC::CallFrame*, JSC::EncodedJSValue, int*);
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructError(JSC::JSGlobalObject*, JSC::CallFrame*) SYSV_ABI;
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructJSON(JSC::JSGlobalObject*, JSC::CallFrame*) SYSV_ABI;
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructRender(JSC::JSGlobalObject*, JSC::CallFrame*) SYSV_ABI;
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructRedirect(JSC::JSGlobalObject*, JSC::CallFrame*) SYSV_ABI;
extern JSC_CALLCONV size_t Response__estimatedSize(void* ptr);
bool isJSXElement(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* globalObject)
{
auto& vm = JSC::getVM(globalObject);
// React does this:
// export const REACT_LEGACY_ELEMENT_TYPE: symbol = Symbol.for('react.element');
// export const REACT_ELEMENT_TYPE: symbol = renameElementSymbol
// ? Symbol.for('react.transitional.element')
// : REACT_LEGACY_ELEMENT_TYPE;
// TODO: cache these, i cri everytim
auto react_legacy_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.element"_s));
auto react_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.transitional.element"_s));
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
// TODO: primitive values (strings, numbers, booleans, null, undefined) are also valid
if (value.isObject()) {
auto scope = DECLARE_THROW_SCOPE(vm);
JSC::JSObject* object = value.getObject();
auto typeofProperty = JSC::Identifier::fromString(vm, "$$typeof"_s);
JSC::JSValue typeofValue = object->get(globalObject, typeofProperty);
RETURN_IF_EXCEPTION(scope, false);
if (typeofValue.isSymbol() && (typeofValue == react_legacy_element_symbol || typeofValue == react_element_symbol)) {
return true;
}
}
return false;
}
extern "C" bool JSC__JSValue__isJSXElement(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* globalObject)
{
return isJSXElement(JSValue0, globalObject);
}
extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES Response__createForSSR(Zig::GlobalObject* globalObject, void* ptr, uint8_t kind)
{
Structure* structure = globalObject->JSBakeResponseStructure();
printf("Creating JSBakeResponse for kind: %d\n", kind);
JSBakeResponse* instance = JSBakeResponse::create(globalObject->vm(), globalObject, structure, ptr);
if (kind == JSBakeResponseKind::Render) {
instance->kind(JSBakeResponseKind::Render);
} else if (kind == JSBakeResponseKind::Redirect) {
instance->kind(JSBakeResponseKind::Redirect);
} else {
// Should not be called with JSBakeResponseKind::Regular or anything
// else
UNREACHABLE();
}
instance->setToThrow(globalObject, globalObject->vm());
return JSValue::encode(instance);
}
static const HashTableValue JSBakeResponseConstructorTableValues[] = {
{ "error"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponseClass__constructError, 0 } },
{ "json"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponseClass__constructJSON, 0 } },
{ "render"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBakeResponseConstructorRender, 1 } },
{ "redirect"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBakeResponseConstructorRedirect, 1 } },
{ "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 } }
};
static const HashTableValue JSBakeResponsePrototypeTableValues[] = {
{ "$$typeof"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetSymbolFor, nullptr } },
{ "type"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetType, nullptr } },
{ "key"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetKey, nullptr } },
{ "props"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetProps, nullptr } },
{ "_store"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetStore, nullptr } },
{ "_owner"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetOwner, nullptr } },
{ "_debugInfo"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetDebugInfo, nullptr } },
{ "_debugStack"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetDebugStack, nullptr } },
{ "_debugTask"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetDebugTask, nullptr } }
};
JSBakeResponse* JSBakeResponse::create(JSC::VM& vm, JSC::Structure* structure, void* ctx)
JSBakeResponse* JSBakeResponse::create(JSC::VM& vm, Zig::GlobalObject* globalObject, JSC::Structure* structure, void* ctx)
{
JSBakeResponse* ptr = new (NotNull, JSC::allocateCell<JSBakeResponse>(vm)) JSBakeResponse(vm, structure, ctx);
ptr->finishCreation(vm);
auto builtinNames = WebCore::builtinNames(vm);
// $$typeof = Symbol.for("react.transitional.element")
ptr->putDirect(vm, builtinNames.$$typeofPublicName(), JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.transitional.element"_s)), 0);
// type = false
ptr->putDirect(vm, builtinNames.typePublicName(), JSC::jsNull(), 0);
// key = null
ptr->putDirect(vm, builtinNames.keyPublicName(), JSC::jsNull(), 0);
// props = {}
ptr->putDirect(vm, builtinNames.propsPublicName(), JSC::constructEmptyObject(globalObject), 0);
// _store = { _validated: 0 }
JSObject* storeObject = JSC::constructEmptyObject(globalObject);
auto validatedIdent = JSC::Identifier::fromString(vm, "validated"_s);
storeObject->putDirect(vm, builtinNames.validatedPublicName(), jsNumber(0), 0);
ptr->putDirect(vm, builtinNames._storePublicName(), storeObject, 0);
// _owner = null
ptr->putDirect(vm, builtinNames._ownerPublicName(), JSC::jsNull(), 0);
// _debugInfo = null
ptr->putDirect(vm, builtinNames._debugInfoPublicName(), JSC::jsNull(), 0);
// _debugStack = null
ptr->putDirect(vm, builtinNames._debugStackPublicName(), JSC::jsNull(), 0);
// _debugTask = null
ptr->putDirect(vm, builtinNames._debugTaskPublicName(), JSC::jsNull(), 0);
return ptr;
}
@@ -103,47 +178,6 @@ void JSBakeResponse::visitChildrenImpl(JSCell* cell, Visitor& visitor)
DEFINE_VISIT_CHILDREN(JSBakeResponse);
class JSBakeResponsePrototype final : public JSNonFinalObject {
public:
using Base = JSNonFinalObject;
static JSBakeResponsePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
{
auto* ptr = new (NotNull, JSC::allocateCell<JSBakeResponsePrototype>(vm)) JSBakeResponsePrototype(vm, structure);
ptr->finishCreation(vm, globalObject);
return ptr;
}
static Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
auto* structure = Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), NonArray);
structure->setMayBePrototype(true);
return structure;
}
DECLARE_INFO;
template<typename CellType, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSBakeResponsePrototype, Base);
return &vm.plainObjectSpace();
}
private:
JSBakeResponsePrototype(JSC::VM& vm, JSC::Structure* structure)
: Base(vm, structure)
{
}
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
{
Base::finishCreation(vm);
reifyStaticProperties(vm, JSBakeResponse::info(), JSBakeResponsePrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
}
};
JSC_DECLARE_HOST_FUNCTION(callBakeResponse);
JSC_DECLARE_HOST_FUNCTION(constructBakeResponse);
@@ -159,8 +193,8 @@ public:
return constructor;
}
// DECLARE_INFO;
DECLARE_EXPORT_INFO;
DECLARE_INFO;
// DECLARE_EXPORT_INFO;
// Must be defined for each specialization class.
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
@@ -169,7 +203,7 @@ public:
JSC::VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSObject* newTarget = asObject(callFrame->newTarget());
auto* constructor = globalObject->JSResponseConstructor();
auto* constructor = globalObject->JSBakeResponseConstructor();
Structure* structure = globalObject->JSBakeResponseStructure();
if (constructor != newTarget) [[unlikely]] {
auto* functionGlobalObject = defaultGlobalObject(
@@ -180,9 +214,10 @@ public:
RETURN_IF_EXCEPTION(scope, {});
}
JSBakeResponse* instance = JSBakeResponse::create(vm, structure, nullptr);
JSBakeResponse* instance = JSBakeResponse::create(vm, globalObject, structure, nullptr);
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, JSValue::encode(instance));
int arg_was_jsx = 0;
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, JSValue::encode(instance), &arg_was_jsx);
if (scope.exception()) [[unlikely]] {
ASSERT_WITH_MESSAGE(!ptr, "Memory leak detected: new SSRResponse() allocated memory without checking for exceptions.");
return JSValue::encode(JSC::jsUndefined());
@@ -190,6 +225,12 @@ public:
instance->m_ctx = ptr;
if (arg_was_jsx == 1 && callFrame->argumentCount() > 0) {
JSValue arg = callFrame->argument(0);
JSValue responseOptions = callFrame->argumentCount() > 1 ? callFrame->argument(1) : JSC::jsUndefined();
instance->wrapInnerComponent(globalObject, vm, arg, responseOptions);
}
auto size = Response__estimatedSize(ptr);
vm.heap.reportExtraMemoryAllocated(instance, size);
@@ -204,9 +245,9 @@ public:
auto scope = DECLARE_THROW_SCOPE(vm);
Structure* structure = globalObject->JSBakeResponseStructure();
JSBakeResponse* instance = JSBakeResponse::create(vm, structure, nullptr);
JSBakeResponse* instance = JSBakeResponse::create(vm, globalObject, structure, nullptr);
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, JSValue::encode(instance));
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, JSValue::encode(instance), nullptr);
if (scope.exception()) [[unlikely]] {
ASSERT_WITH_MESSAGE(!ptr, "Memory leak detected: new SSRResponse() allocated memory without checking for exceptions.");
return JSValue::encode(JSC::jsUndefined());
@@ -247,130 +288,32 @@ private:
}
};
const JSC::ClassInfo JSBakeResponsePrototype::s_info = { "SSRResponse"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponsePrototype) };
const JSC::ClassInfo JSBakeResponse::s_info = { "SSRResponse"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponse) };
const JSC::ClassInfo JSBakeResponseConstructor::s_info = { "SSRResponse"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponseConstructor) };
const JSC::ClassInfo JSBakeResponse::s_info = { "SSRResponse"_s, &JSResponse::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponse) };
const JSC::ClassInfo JSBakeResponseConstructor::s_info = { ""_s, &JSC::InternalFunction::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponseConstructor) };
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetSymbolFor, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
Structure* createJSBakeResponseStructure(JSC::VM& vm, Zig::GlobalObject* globalObject, JSObject* prototype)
{
JSBakeResponse* response = jsDynamicCast<JSBakeResponse*>(JSValue::decode(thisValue));
if (!response)
return JSValue::encode(jsUndefined());
auto& vm = globalObject->vm();
auto symbolKey = "react.transitional.element"_s;
return JSValue::encode(JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey(symbolKey)));
}
auto structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, 0), JSBakeResponse::info(), NonArray, 0);
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetType, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
JSBakeResponse* response = jsDynamicCast<JSBakeResponse*>(JSValue::decode(thisValue));
if (!response)
return JSValue::encode(jsUndefined());
// Unfortunately we cannot use structure->addPropertyTransition as it does
// not with with JSC::JSNonFinalObject
printf("m_ctx: %p\n", response->m_ctx);
// auto& type = response->type();
// auto typeValue = type.get();
// return JSValue::encode(typeValue);
String wtfstring = "Hello"_s;
auto* jsstring = JSC::jsString(globalObject->vm(), wtfstring);
return JSValue::encode(jsstring);
}
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetKey, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
return JSValue::encode(jsNull());
}
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetProps, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
JSBakeResponse* response = jsDynamicCast<JSBakeResponse*>(JSValue::decode(thisValue));
if (!response)
return JSValue::encode(jsUndefined());
return JSValue::encode(JSC::constructEmptyObject(globalObject));
}
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetStore, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
JSBakeResponse* response = jsDynamicCast<JSBakeResponse*>(JSValue::decode(thisValue));
if (!response)
return JSValue::encode(jsUndefined());
auto& vm = globalObject->vm();
JSObject* storeObject = JSC::constructEmptyObject(globalObject);
auto validatedIdent = JSC::Identifier::fromString(vm, "validated"_s);
storeObject->putDirect(vm, validatedIdent, jsNumber(0), 0);
return JSValue::encode(storeObject);
}
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetOwner, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
return JSValue::encode(jsNull());
}
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugInfo, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
return JSValue::encode(jsNull());
}
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugStack, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
return JSValue::encode(jsNull());
}
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugTask, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
return JSValue::encode(jsNull());
}
JSC_DEFINE_HOST_FUNCTION(jsBakeResponseConstructorRender, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
return JSValue::encode(jsUndefined());
}
JSC_DEFINE_HOST_FUNCTION(jsBakeResponseConstructorRedirect, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
return JSValue::encode(jsUndefined());
}
JSC_DEFINE_HOST_FUNCTION(callBakeResponse, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
auto& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
throwScope.throwException(globalObject, createTypeError(globalObject, "BakeResponse constructor cannot be called as a function"_s));
return JSValue::encode(jsUndefined());
}
JSC_DEFINE_HOST_FUNCTION(constructBakeResponse, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
auto& vm = globalObject->vm();
auto* zigGlobalObject = defaultGlobalObject(globalObject);
auto* structure = createJSBakeResponseStructure(vm, zigGlobalObject);
return JSValue::encode(JSBakeResponse::create(vm, structure, nullptr));
return structure;
}
void setupJSBakeResponseClassStructure(JSC::LazyClassStructure::Initializer& init)
{
auto* zigGlobal = reinterpret_cast<Zig::GlobalObject*>(init.global);
auto* prototypeStructure = JSBakeResponsePrototype::createStructure(init.vm, init.global, zigGlobal->JSResponsePrototype());
auto* prototype = JSBakeResponsePrototype::create(init.vm, init.global, prototypeStructure);
auto* prototype = JSC::constructEmptyObject(zigGlobal, zigGlobal->JSResponsePrototype());
auto* constructorStructure = JSBakeResponseConstructor::createStructure(init.vm, init.global, init.global->functionPrototype());
auto* constructor = JSBakeResponseConstructor::create(init.vm, constructorStructure, prototype);
auto* structure = JSBakeResponse::createStructure(init.vm, init.global, prototype);
auto* structure = createJSBakeResponseStructure(init.vm, zigGlobal, prototype);
init.setPrototype(prototype);
init.setStructure(structure);
init.setConstructor(constructor);
}
Structure* createJSBakeResponseStructure(JSC::VM& vm, Zig::GlobalObject* globalObject)
{
return globalObject->JSBakeResponseStructure();
}
} // namespace Bun

View File

@@ -8,14 +8,24 @@ namespace Bun {
using namespace JSC;
using namespace WebCore;
enum JSBakeResponseKind : uint8_t {
Regular = 0,
Redirect = 1,
Render = 2,
};
class JSBakeResponse : public JSResponse {
public:
using Base = JSResponse;
static JSBakeResponse* create(JSC::VM& vm, JSC::Structure* structure, void* ctx);
DECLARE_VISIT_CHILDREN;
DECLARE_INFO;
static JSBakeResponse* create(JSC::VM& vm, Zig::GlobalObject* globalObject, JSC::Structure* structure, void* ctx);
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype);
// JSC::Strong<JSC::Unknown>& type() { return m_type; }
JSBakeResponseKind kind() const { return m_kind; }
void kind(JSBakeResponseKind kind) { m_kind = kind; }
template<typename, JSC::SubspaceAccess mode>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
@@ -29,17 +39,54 @@ public:
static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
DECLARE_VISIT_CHILDREN;
DECLARE_INFO;
void setToThrow(JSC::JSGlobalObject* globalObject, JSC::VM& vm)
{
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
JSC::MarkedArgumentBuffer args;
// component
args.append(jsUndefined());
// responseObject
args.append(this);
// responseOptions
args.append(jsUndefined());
// kind
args.append(JSC::jsNumber(static_cast<unsigned char>(this->kind())));
auto callData = JSC::getCallData(wrapComponentFn);
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
this->putDirect(vm, WebCore::builtinNames(vm).typePublicName(), wrappedComponent, 0);
}
void wrapInnerComponent(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSValue component, JSValue responseOptions)
{
this->kind(JSBakeResponseKind::Regular);
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
JSC::MarkedArgumentBuffer args;
// component
args.append(component);
// responseObject
args.append(this);
// responseOptions
args.append(responseOptions);
// kind
args.append(JSC::jsNumber(static_cast<unsigned char>(JSBakeResponseKind::Regular)));
auto callData = JSC::getCallData(wrapComponentFn);
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
this->putDirect(vm, WebCore::builtinNames(vm).typePublicName(), wrappedComponent, 0);
}
private:
JSBakeResponse(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr);
void finishCreation(JSC::VM& vm);
// JSC::Strong<JSC::Unknown> m_type;
JSBakeResponseKind m_kind;
};
JSC::Structure* createJSBakeResponseStructure(JSC::VM&, Zig::GlobalObject*);
void setupJSBakeResponseClassStructure(JSC::LazyClassStructure::Initializer& init);
} // namespace Bun

View File

@@ -67,11 +67,6 @@ pub const JSValue = enum(i64) {
JSC__JSValue__transformToReactElement(responseValue, componentValue, globalThis);
}
extern fn JSC__JSValue__transformToReactElementWithOptions(responseValue: JSValue, componentValue: JSValue, responseOptions: JSValue, globalObject: *JSGlobalObject) void;
pub fn transformToReactElementWithOptions(responseValue: JSValue, componentValue: JSValue, responseOptions: JSValue, globalThis: *jsc.JSGlobalObject) JSError!void {
return bun.jsc.fromJSHostCallGeneric(globalThis, @src(), JSC__JSValue__transformToReactElementWithOptions, .{ responseValue, componentValue, responseOptions, globalThis });
}
extern fn JSC__JSValue__getDirectIndex(JSValue, *JSGlobalObject, u32) JSValue;
pub fn getDirectIndex(this: JSValue, globalThis: *JSGlobalObject, i: u32) JSValue {
return JSC__JSValue__getDirectIndex(this, globalThis, i);

View File

@@ -234,9 +234,8 @@ public:
JSC::JSValue HTTPSResponseSinkPrototype() const { return m_JSHTTPSResponseSinkClassStructure.prototypeInitializedOnMainThread(this); }
JSC::JSValue JSReadableHTTPSResponseSinkControllerPrototype() const { return m_JSHTTPSResponseControllerPrototype.getInitializedOnMainThread(this); }
JSC::JSObject* JSBakeResponseConstructor() const { return m_JSBakeResponseClassStructure.constructorInitializedOnMainThread(this); }
JSC::Structure* JSBakeResponseStructure() const { return m_JSBakeResponseClassStructure.getInitializedOnMainThread(this); }
JSC::JSObject* JSBakeResponseConstructor() { return m_JSBakeResponseClassStructure.constructorInitializedOnMainThread(this); }
JSC::JSValue JSBakeResponsePrototype() const { return m_JSBakeResponseClassStructure.prototypeInitializedOnMainThread(this); }
JSC::Structure* NetworkSinkStructure() const { return m_JSNetworkSinkClassStructure.getInitializedOnMainThread(this); }
JSC::JSObject* NetworkSink() { return m_JSNetworkSinkClassStructure.constructorInitializedOnMainThread(this); }

View File

@@ -5888,300 +5888,6 @@ restart:
JSC__JSValue__forEachPropertyImpl<false>(JSValue0, globalObject, arg2, iter);
}
extern "C" bool JSC__JSValue__isJSXElement(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* globalObject)
{
auto& vm = JSC::getVM(globalObject);
// React does this:
// export const REACT_LEGACY_ELEMENT_TYPE: symbol = Symbol.for('react.element');
// export const REACT_ELEMENT_TYPE: symbol = renameElementSymbol
// ? Symbol.for('react.transitional.element')
// : REACT_LEGACY_ELEMENT_TYPE;
// TODO: cache these, i cri everytim
auto react_legacy_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.element"_s));
auto react_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.transitional.element"_s));
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
// TODO: primitive values (strings, numbers, booleans, null, undefined) are also valid
if (value.isObject()) {
auto scope = DECLARE_THROW_SCOPE(vm);
JSC::JSObject* object = value.getObject();
auto typeofProperty = JSC::Identifier::fromString(vm, "$$typeof"_s);
JSC::JSValue typeofValue = object->get(globalObject, typeofProperty);
RETURN_IF_EXCEPTION(scope, false);
if (typeofValue.isSymbol() && (typeofValue == react_legacy_element_symbol || typeofValue == react_element_symbol)) {
return true;
}
}
return false;
}
// Forward declaration
extern "C" void JSC__JSValue__transformToReactElementWithOptions(JSC::EncodedJSValue responseValue, JSC::EncodedJSValue componentValue, JSC::EncodedJSValue responseOptionsValue, JSC::JSGlobalObject* globalObject);
extern "C" void JSC__JSValue__transformToReactElement(JSC::EncodedJSValue responseValue, JSC::EncodedJSValue componentValue, JSC::JSGlobalObject* globalObject)
{
// Call the version with options, passing undefined for options
JSC__JSValue__transformToReactElementWithOptions(responseValue, componentValue, JSC::JSValue::encode(JSC::jsUndefined()), globalObject);
}
/// TODO: This could just be a builtin function and be 10x less lines of code why is it in C++
/// TODO: this should actually just be a special Response sub-class and the transpiler rewrites the code to use this
///
/// What this function does is make a Response object pretend to be a React
/// component. To do this we have to put a couple properties on it:
///
/// ```ts
/// response["$$typeof"] = REACT_ELEMENT_TYPE;
/// response.type = Component;
/// response.key = null;
/// response.props = {};
/// // Add the _store object that React expects in dev mode
/// response._store = {};
/// Object.defineProperty(response._store, 'validated', {
/// configurable: false,
/// enumerable: false,
/// writable: true,
/// value: 0 // or 1 to mark it as already validated
/// });
/// // Add debug properties expected in dev mode
/// response._owner = null;
/// response._debugInfo = null;
/// response._debugStack = null; // or new Error() if you want a stack trace
/// response._debugTask = null;
/// ```
extern "C" void JSC__JSValue__transformToReactElementWithOptions(JSC::EncodedJSValue responseValue, JSC::EncodedJSValue componentValue, JSC::EncodedJSValue responseOptionsValue, JSC::JSGlobalObject* globalObject)
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_CATCH_SCOPE(vm);
JSC::JSValue response = JSC::JSValue::decode(responseValue);
JSC::JSValue component = JSC::JSValue::decode(componentValue);
JSC::JSValue responseOptions = JSC::JSValue::decode(responseOptionsValue);
if (!response.isObject()) {
return;
}
JSC::JSObject* responseObject = response.getObject();
// Get the React element symbol - same as in isJSXElement
// For now, use the transitional element symbol (React 19+)
// TODO: check for legacy symbol as fallback
auto react_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.transitional.element"_s));
// If we have response options, we need to wrap the component
// For now, we'll store the response options directly on the response object
// The actual wrapping with AsyncLocalStorage update will happen when rendered
if (!responseOptions.isUndefinedOrNull() && responseOptions.isObject()) {
// Store the response options on the response object for later use
// This will be accessed when the component is rendered
auto responseOptionsIdentifier = JSC::Identifier::fromString(vm, "__responseOptions"_s);
responseObject->putDirect(vm, responseOptionsIdentifier, responseOptions, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
}
// Transform the Response object itself into a React element
// The component parameter is what will be stored in the 'type' field
// Set $$typeof property to mark this as a React element
auto typeofIdentifier = JSC::Identifier::fromString(vm, "$$typeof"_s);
responseObject->putDirect(vm, typeofIdentifier, react_element_symbol, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
// Set type property
auto typeIdentifier = JSC::Identifier::fromString(vm, "type"_s);
// TODO: this is fucking awful
// Check if this is a render redirect (Response.render() case) or regular redirect (Response.redirect() case)
// We detect this by checking if component is the special marker string "__bun_render_redirect__" or "__bun_redirect__"
bool isRenderRedirect = false;
bool isRedirect = false;
if (component.isString()) {
JSC::JSString* componentString = component.toString(globalObject);
if (componentString) {
String componentStr = componentString->value(globalObject);
if (componentStr == "__bun_render_redirect__"_s) {
isRenderRedirect = true;
} else if (componentStr == "__bun_redirect__"_s) {
isRedirect = true;
}
}
}
// Handle Response.render() case - use BakeSSRResponse builtin to create wrapper
if (isRenderRedirect) {
// Get the render path and params from the response object
auto renderPathIdentifier = JSC::Identifier::fromString(vm, "__renderPath"_s);
auto renderParamsIdentifier = JSC::Identifier::fromString(vm, "__renderParams"_s);
JSC::JSValue renderPath = responseObject->get(globalObject, renderPathIdentifier);
JSC::JSValue renderParams = responseObject->get(globalObject, renderParamsIdentifier);
// Use the BakeSSRResponse builtin's wrapComponent function
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
// Call wrapComponent(path, params, true, responseObject) where true indicates this is a render redirect
JSC::MarkedArgumentBuffer args;
args.append(renderPath);
args.append(renderParams);
args.append(JSC::jsBoolean(true)); // This is a render redirect
args.append(response); // Pass the Response object
// Call the wrapComponent function
auto callData = JSC::getCallData(wrapComponentFn);
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
if (!scope.exception() && !wrappedComponent.isUndefinedOrNull()) {
responseObject->putDirect(vm, typeIdentifier, wrappedComponent, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
} else {
// If there was an error, clear it and set type to null
scope.clearException();
responseObject->putDirect(vm, typeIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
}
}
// Handle Response.redirect() case - use BakeSSRResponse builtin to create wrapper
else if (isRedirect) {
// Use the BakeSSRResponse builtin's wrapComponent function
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
// Call wrapComponent(undefined, undefined, "redirect", responseObject)
// For redirect, we pass "redirect" as the third parameter and the Response object as the fourth
JSC::MarkedArgumentBuffer args;
args.append(JSC::jsUndefined()); // No component for redirect
args.append(JSC::jsUndefined()); // No response options for redirect
// Pass "redirect" string as the third parameter to indicate this is a redirect
// The responseOptions parameter from transformToReactElementWithOptions contains "redirect"
args.append(responseOptions); // This should be the "redirect" string
args.append(response); // Pass the Response object
// Call the wrapComponent function
auto callData = JSC::getCallData(wrapComponentFn);
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
if (!scope.exception() && !wrappedComponent.isUndefinedOrNull()) {
responseObject->putDirect(vm, typeIdentifier, wrappedComponent, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
} else {
// If there was an error, clear it and set type to null
scope.clearException();
responseObject->putDirect(vm, typeIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
}
}
// TODO: this is stupid
// Check if the component is a JSX element (has $$typeof property)
// If it is, we need to wrap it in a function for React to work properly
// because `<Component />` is already the result of calling Component()
else if (component.isObject()) {
JSC::JSObject* componentObject = component.getObject();
auto typeofProperty = JSC::Identifier::fromString(vm, "$$typeof"_s);
JSC::JSValue typeofValue = componentObject->get(globalObject, typeofProperty);
if (!scope.exception() && typeofValue.isSymbol()) {
// It's a JSX element - wrap it in a function that returns it
// If we have response options, use the BakeSSRResponse builtin to wrap the component
// so it can update AsyncLocalStorage when rendered
if (!responseOptions.isUndefinedOrNull() && responseOptions.isObject()) {
// Use the BakeSSRResponse builtin's wrapComponent function
// This will create a wrapper that updates AsyncLocalStorage before returning the component
// Create the wrapComponent function from the BakeSSRResponse builtin
// The pattern is: <filename><functionName>CodeGenerator
// So for BakeSSRResponse.ts with export function wrapComponent:
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
// Call wrapComponent(component, responseOptions, false, undefined)
JSC::MarkedArgumentBuffer args;
args.append(component);
args.append(responseOptions);
args.append(JSC::jsBoolean(false)); // Not a render redirect
args.append(JSC::jsUndefined()); // No response object for regular case
// Call the wrapComponent function
auto callData = JSC::getCallData(wrapComponentFn);
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
if (!scope.exception() && !wrappedComponent.isUndefinedOrNull()) {
responseObject->putDirect(vm, typeIdentifier, wrappedComponent, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
} else {
// Fall back to simple wrapper if there was an exception or undefined result
scope.clearException();
JSC::Strong<JSC::Unknown> strongComponent(vm, component);
auto* wrapperFunction = JSC::JSNativeStdFunction::create(
vm,
globalObject,
0, // arity
String(), // name
[strongComponent](JSC::JSGlobalObject* execGlobalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
return JSC::JSValue::encode(strongComponent.get());
});
responseObject->putDirect(vm, typeIdentifier, wrapperFunction, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
}
} else {
// No response options - create a simple wrapper
JSC::Strong<JSC::Unknown> strongComponent(vm, component);
auto* wrapperFunction = JSC::JSNativeStdFunction::create(
vm,
globalObject,
0, // arity
String(), // name
[strongComponent](JSC::JSGlobalObject* execGlobalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
return JSC::JSValue::encode(strongComponent.get());
});
responseObject->putDirect(vm, typeIdentifier, wrapperFunction, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
}
} else {
// It's not a JSX element, use it directly
responseObject->putDirect(vm, typeIdentifier, component, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
}
} else {
// It's not an object (could be a function or primitive), use it directly
responseObject->putDirect(vm, typeIdentifier, component, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
}
// Set key property to null
auto keyIdentifier = JSC::Identifier::fromString(vm, "key"_s);
responseObject->putDirect(vm, keyIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
// Set props property to empty object
auto propsIdentifier = JSC::Identifier::fromString(vm, "props"_s);
responseObject->putDirect(vm, propsIdentifier, JSC::constructEmptyObject(globalObject), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
// Add _store object for dev mode
auto storeIdentifier = JSC::Identifier::fromString(vm, "_store"_s);
JSC::JSObject* storeObject = JSC::constructEmptyObject(globalObject);
// Add validated property to _store
auto validatedIdentifier = JSC::Identifier::fromString(vm, "validated"_s);
storeObject->putDirect(vm, validatedIdentifier, JSC::jsNumber(0), 0);
responseObject->putDirect(vm, storeIdentifier, storeObject, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
// Add debug properties (all set to null)
auto ownerIdentifier = JSC::Identifier::fromString(vm, "_owner"_s);
responseObject->putDirect(vm, ownerIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
auto debugInfoIdentifier = JSC::Identifier::fromString(vm, "_debugInfo"_s);
responseObject->putDirect(vm, debugInfoIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
auto debugStackIdentifier = JSC::Identifier::fromString(vm, "_debugStack"_s);
// TODO: we should put an error here if we want a stack trace apparently
responseObject->putDirect(vm, debugStackIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
auto debugTaskIdentifier = JSC::Identifier::fromString(vm, "_debugTask"_s);
responseObject->putDirect(vm, debugTaskIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
}
extern "C" void JSC__JSValue__forEachPropertyNonIndexed(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* globalObject, void* arg2, void (*iter)(JSC::JSGlobalObject* arg0, void* ctx, ZigString* arg2, JSC::EncodedJSValue JSValue3, bool isSymbol, bool isPrivateSymbol))
{
JSC__JSValue__forEachPropertyImpl<true>(JSValue0, globalObject, arg2, iter);

View File

@@ -66,6 +66,21 @@ 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: SSRKind) JSValue;
pub fn toJSForSSR(this: *Response, globalObject: *JSGlobalObject, kind: SSRKind) JSValue {
this.calculateEstimatedByteSize();
return Response__createForSSR(globalObject, this, kind);
}
pub fn getBodyValue(
this: *Response,
) *Body.Value {
@@ -515,25 +530,19 @@ pub fn constructRedirect(
try headers_ref.put(.Location, url_string_slice.slice(), globalThis);
const ptr = bun.new(Response, response);
const response_js = ptr.toJS(globalThis);
// Check if dev_server_async_local_storage is set (indicating we're in Bun dev server)
const vm = globalThis.bunVM();
// Check if dev_server_async_local_storage is set (indicating we're in Bun dev server)
if (vm.dev_server_async_local_storage.get()) |async_local_storage| {
// Mark this as a redirect Response that should be handled specially
// when used in a React component
const redirect_marker = ZigString.init("__bun_redirect__").toJS(globalThis);
// Transform the Response to act as a React element with special redirect handling
// Pass "redirect" as the third parameter to indicate this is a redirect
const redirect_flag = ZigString.init("redirect").toJS(globalThis);
try assertStreamingDisabled(globalThis, async_local_storage, "Response.redirect");
try JSValue.transformToReactElementWithOptions(response_js, redirect_marker, redirect_flag, globalThis);
return ptr.toJSForSSR(globalThis, .redirect);
}
const response_js = ptr.toJS(globalThis);
return response_js;
}
/// This function is only available on JSBakeResponse
pub fn constructRender(
globalThis: *jsc.JSGlobalObject,
callframe: *jsc.CallFrame,
@@ -582,25 +591,9 @@ pub fn constructRender(
},
});
const response_js = response.toJS(globalThis);
const response_js = response.toJSForSSR(globalThis, .render);
response_js.ensureStillAlive();
// Store the render path and params on the response for later use
// When React tries to render this component, we'll check for these and throw RenderAbortError
response_js.put(globalThis, "__renderPath", path_arg);
const params_arg = if (arguments.len >= 2) arguments.ptr[1] else JSValue.jsNull();
response_js.put(globalThis, "__renderParams", params_arg);
// TODO: this is terrible
// Create a simple wrapper function that will be called by React
// This needs to be handled specially in transformToReactElementWithOptions
// We'll pass a special marker as the component to indicate this is a render redirect
const render_marker = ZigString.init("__bun_render_redirect__").toJS(globalThis);
// Transform the Response to act as a React element
// The C++ code will need to check for this special marker
try JSValue.transformToReactElementWithOptions(response_js, render_marker, params_arg, globalThis);
return response_js;
}
@@ -624,11 +617,11 @@ pub fn constructError(
}
pub fn constructor(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue) bun.JSError!*Response {
return constructorImpl(globalThis, callframe, this_value, false);
return constructorImpl(globalThis, callframe, this_value, null);
}
pub fn ResponseClass__constructForSSR(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame, thisValue: jsc.JSValue) callconv(jsc.conv) ?*anyopaque {
return @as(*Response, Response.constructor(globalObject, callFrame, thisValue) catch |err| switch (err) {
pub fn ResponseClass__constructForSSR(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame, thisValue: jsc.JSValue, bake_ssr_has_jsx: ?*c_int) callconv(jsc.conv) ?*anyopaque {
return @as(*Response, Response.constructorForSSR(globalObject, callFrame, thisValue, bake_ssr_has_jsx) catch |err| switch (err) {
error.JSError => return null,
error.OutOfMemory => {
globalObject.throwOutOfMemory() catch {};
@@ -641,11 +634,11 @@ comptime {
@export(&ResponseClass__constructForSSR, .{ .name = "ResponseClass__constructForSSR" });
}
pub fn constructorForSSR(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue) bun.JSError!*Response {
return constructorImpl(globalThis, callframe, this_value);
pub fn constructorForSSR(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue, bake_ssr_has_jsx: ?*c_int) bun.JSError!*Response {
return constructorImpl(globalThis, callframe, this_value, bake_ssr_has_jsx);
}
pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue, bake_ssr_response_enabled: bool) bun.JSError!*Response {
pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue, bake_ssr_has_jsx: ?*c_int) bun.JSError!*Response {
var arguments = callframe.argumentsAsArray(2);
if (!arguments[0].isUndefinedOrNull() and arguments[0].isObject()) {
@@ -683,7 +676,15 @@ pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFram
// Special case for bake: allow `return new Response(<jsx> ... </jsx>, { ... }`
// inside of a react component
if (bake_ssr_response_enabled and globalThis.allowJSXInResponseConstructor()) {
if (bake_ssr_has_jsx != null) {
bake_ssr_has_jsx.?.* = 0;
if (globalThis.allowJSXInResponseConstructor() and try arguments[0].isJSXElement(globalThis)) {
const vm = globalThis.bunVM();
if (vm.dev_server_async_local_storage.get()) |async_local_storage| {
try assertStreamingDisabled(globalThis, async_local_storage, "new Response(<jsx />, { ... })");
}
bake_ssr_has_jsx.?.* = 1;
}
_ = this_value;
// const arg = arguments[0];
// // Check if it's a JSX element (object with $$typeof)
@@ -695,7 +696,7 @@ pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFram
// // Pass the response options (arguments[1]) to transformToReactElement
// // so it can store them for later use when the component is rendered
// const responseOptions = if (arguments[1].isObject()) arguments[1] else .js_undefined;
// const responseOptions = if (arguments[1].isObject()) adirectrguments[1] else .js_undefined;
// try JSValue.transformToReactElementWithOptions(this_value, arg, responseOptions, globalThis);
// }
}

View File

@@ -1,14 +1,9 @@
// Used to make a Response fake being a component
// When this is called, it will render the component and then update async local
// storage with the options of the Response
// For Response.render(), we pass the response as strongComponent and need a 4th parameter
// For Response.redirect(), isRenderRedirect will be "redirect" instead of true
export function wrapComponent(strongComponent, responseOptions, isRenderRedirect, responseObject) {
export function wrapComponent(component, responseObject, responseOptions, kind) {
const bakeGetAsyncLocalStorage = $newZigFunction("bun.js/webcore/Response.zig", "bakeGetAsyncLocalStorage", 0);
return function () {
// For Response.redirect(), we need to throw a RedirectAbortError
if (isRenderRedirect === "redirect") {
if (kind === 1 /* JSBakeResponseKind.Redirect */) {
// responseObject is the Response from Response.redirect()
const RedirectAbortError = globalThis.RedirectAbortError;
if (RedirectAbortError) {
@@ -19,12 +14,12 @@ export function wrapComponent(strongComponent, responseOptions, isRenderRedirect
}
// For Response.render(), we need to throw a RenderAbortError
if (isRenderRedirect === true || isRenderRedirect === "render") {
if (kind === 2 /* JSBakeResponseKind.Render */) {
// strongComponent is the path string, responseOptions is params, responseObject is the Response
// We need to get the RenderAbortError from the global
const RenderAbortError = globalThis.RenderAbortError;
if (RenderAbortError) {
throw new RenderAbortError(strongComponent, responseOptions, responseObject);
throw new RenderAbortError(component, responseOptions, responseObject);
}
// Fallback if RenderAbortError is not available
throw new Error("RenderAbortError not available this is a bug");
@@ -38,6 +33,6 @@ export function wrapComponent(strongComponent, responseOptions, isRenderRedirect
store.responseOptions = responseOptions;
}
}
return strongComponent;
return component;
};
}

View File

@@ -281,6 +281,16 @@ using namespace JSC;
macro(writer) \
macro(writing) \
macro(written) \
macro($$typeof) \
macro(type) \
macro(key) \
macro(props) \
macro(validated) \
macro(_store) \
macro(_owner) \
macro(_debugInfo) \
macro(_debugStack) \
macro(_debugTask) \
BUN_ADDITIONAL_BUILTIN_NAMES(macro)
// --- END of BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME ---