Files
bun.sh/src/bun.js/webcore.zig
Zack Radisic a89e61fcaa ssg 3 (#22138)
### What does this PR do?

Fixes a crash related to the dev server overwriting the uws user context
pointer when setting abort callback.

Adds support for `return new Response(<jsx />, { ... })` and `return
Response.render(...)` and `return Response.redirect(...)`:
- Created a `SSRResponse` class to handle this (see
`JSBakeResponse.{h,cpp}`)
- `SSRResponse` is designed to "fake" being a React component 
- This is done in JSBakeResponse::create inside of
src/bun.js/bindings/JSBakeResponse.cpp
- And `src/js/builtins/BakeSSRResponse.ts` defines a `wrapComponent`
function which wraps
the passed in component (when doing `new Response(<jsx />, ...)`). It
does
    this to throw an error (in redirect()/render() case) or return the
    component.
- Created a `BakeAdditionsToGlobal` struct which contains some
properties
    needed for this
- Added some of the properties we need to fake to BunBuiltinNames.h
(e.g.
    `$$typeof`), the rationale behind this is that we couldn't use
`structure->addPropertyTransition` because JSBakeResponse is not a final
    JSObject.
- When bake and server-side, bundler rewrites `Response ->
Bun.SSRResponse` (see `src/ast/P.zig` and `src/ast/visitExpr.zig`)
- Created a new WebCore body variant (`Render: struct { path: []const u8
}`)
  - Created when `return Response.render(...)`
  - When handled, it re-invokes dev server to render the new path

Enables server-side sourcemaps for the dev server:
- New source providers for server-side:
(`DevServerSourceProvider.{h,cpp}`)
- IncrementalGraph and SourceMapStore are updated to support this

There are numerous other stuff:
- allow `app` configuration from Bun.serve(...)
- fix errors stopping dev server
- fix use after free related to in
RequestContext.finishRunningErrorHandler
- Request.cookies
- Make `"use client";` components work
- Fix some bugs using `require(...)` in dev server
- Fix catch-all routes not working in the dev server
- Updates `findSourceMappingURL(...)` to use `std.mem.lastIndexOf(...)`
because
  the sourcemap that should be used is the last one anyway

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alistair Smith <hi@alistair.sh>
2025-09-30 05:26:32 -07:00

129 lines
4.6 KiB
Zig

//! Web APIs implemented in Zig live here
comptime {
if (bun.Environment.export_cpp_apis) {
_ = &@import("./webcore/prompt.zig");
}
_ = &@import("./webcore/TextEncoder.zig");
}
pub const DOMExceptionCode = @import("./bindings/JSErrorCode.zig").DOMExceptionCode;
// TODO: make this JSGlobalObject local for better security
pub const ByteListPool = bun.ObjectPool(bun.ByteList, null, true, 8);
pub const Crypto = @import("./webcore/Crypto.zig");
pub const AbortSignal = @import("./bindings/AbortSignal.zig").AbortSignal;
pub const WebWorker = @import("./web_worker.zig");
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");
pub const encoding = @import("./webcore/encoding.zig");
pub const ReadableStream = @import("./webcore/ReadableStream.zig");
pub const Blob = @import("./webcore/Blob.zig");
pub const S3Stat = @import("./webcore/S3Stat.zig").S3Stat;
pub const ResumableFetchSink = @import("./webcore/ResumableSink.zig").ResumableFetchSink;
pub const ResumableS3UploadSink = @import("./webcore/ResumableSink.zig").ResumableS3UploadSink;
pub const ResumableSinkBackpressure = @import("./webcore/ResumableSink.zig").ResumableSinkBackpressure;
pub const S3Client = @import("./webcore/S3Client.zig").S3Client;
pub const Request = @import("./webcore/Request.zig");
pub const Body = @import("./webcore/Body.zig");
pub const CookieMap = @import("./webcore/CookieMap.zig").CookieMap;
pub const ObjectURLRegistry = @import("./webcore/ObjectURLRegistry.zig");
pub const Sink = @import("./webcore/Sink.zig");
pub const FileSink = @import("./webcore/FileSink.zig");
pub const FetchHeaders = @import("./bindings/FetchHeaders.zig").FetchHeaders;
pub const ByteBlobLoader = @import("./webcore/ByteBlobLoader.zig");
pub const ByteStream = @import("./webcore/ByteStream.zig");
pub const FileReader = @import("./webcore/FileReader.zig");
pub const ScriptExecutionContext = @import("./webcore/ScriptExecutionContext.zig");
pub const streams = @import("./webcore/streams.zig");
pub const NetworkSink = streams.NetworkSink;
pub const HTTPResponseSink = streams.HTTPResponseSink;
pub const HTTPSResponseSink = streams.HTTPSResponseSink;
pub const HTTPServerWritable = streams.HTTPServerWritable;
comptime {
WebSocketClient.exportAll();
WebSocketClientTLS.exportAll();
WebSocketHTTPClient.exportAll();
WebSocketHTTPSClient.exportAll();
}
pub const PathOrFileDescriptor = union(enum) {
path: jsc.ZigString.Slice,
fd: bun.FileDescriptor,
pub fn deinit(this: *const PathOrFileDescriptor) void {
if (this.* == .path) this.path.deinit();
}
};
pub const Pipe = struct {
ctx: ?*anyopaque = null,
onPipe: ?Function = null,
pub inline fn isEmpty(this: *const Pipe) bool {
return this.ctx == null and this.onPipe == null;
}
pub const Function = *const fn (
ctx: *anyopaque,
stream: streams.Result,
allocator: std.mem.Allocator,
) void;
pub fn Wrap(comptime Type: type, comptime function: anytype) type {
return struct {
pub fn pipe(self: *anyopaque, stream: streams.Result, allocator: std.mem.Allocator) void {
function(
@as(*Type, @ptrCast(@alignCast(self))),
stream,
allocator,
);
}
pub fn init(self: *Type) Pipe {
return Pipe{
.ctx = self,
.onPipe = pipe,
};
}
};
}
};
pub const DrainResult = union(enum) {
owned: struct {
list: std.ArrayList(u8),
size_hint: usize,
},
estimated_size: usize,
empty: void,
aborted: void,
};
pub const Lifetime = enum {
clone,
transfer,
share,
/// When reading from a fifo like STDIN/STDERR
temporary,
};
const std = @import("std");
const WebSocketClient = @import("../http/websocket_http_client.zig").WebSocketClient;
const WebSocketClientTLS = @import("../http/websocket_http_client.zig").WebSocketClientTLS;
const WebSocketHTTPClient = @import("../http/websocket_http_client.zig").WebSocketHTTPClient;
const WebSocketHTTPSClient = @import("../http/websocket_http_client.zig").WebSocketHTTPSClient;
const bun = @import("bun");
const jsc = bun.jsc;