Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog.d/9625-bun-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
### Bun compatibility

- **`Bun.serve` and `import { serve } from "bun"` now run on Perry's native
HTTP server.** The facade supports ephemeral ports, hostname binding,
Fetch `Request`/`Response` handlers (including async returns and the `error`
callback), `requestIP`, observable server properties, and
`stop`/`ref`/`unref` lifecycle methods. TLS options fail explicitly with
`ERR_NOT_SUPPORTED` until native TLS support is added.
8 changes: 8 additions & 0 deletions crates/perry-api-manifest/src/entries/part_4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,14 @@ pub(crate) const API_MANIFEST_PART_4: &[ApiEntry] = &[
method("bun", "hash", false, None),
method("bun", "file", false, None),
method("bun", "write", false, None),
method_sig(
"bun",
"serve",
false,
None,
&[p_any("options")],
TypeSpec::Any,
),
method("bun", "pathToFileURL", false, None),
method("bun", "fileURLToPath", false, None),
// #8537 — OpenCode compatibility coverage added these dispatch rows
Expand Down
32 changes: 32 additions & 0 deletions crates/perry-codegen/src/ext_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,26 @@ thread_local! {
/// ~30 ns per emission, fully amortized by the surrounding format!
/// strings.
pub(crate) fn record_ffi_call(symbol: &str) {
// #9603: `js_bun_serve` is implemented by perry-ext-http but crosses a
// narrow JSON bridge into perry-stdlib's Fetch registries to construct
// Request values and consume Response values. It therefore has two
// providers, unlike the ordinary one-symbol/one-owner registry rows.
if symbol == "js_bun_serve" {
if provider_recording_permitted() {
let mut guard = USED_PROVIDERS.lock().expect("USED_PROVIDERS poisoned");
let providers = guard.get_or_insert_with(HashSet::new);
providers.insert(OwnerKind::WellKnown("http"));
providers.insert(OwnerKind::Stdlib {
feature: Some("web-fetch"),
});
}
MODULE_CAPTURE.with(|cell| {
if let Some(set) = cell.borrow_mut().as_mut() {
set.insert("js_bun_serve");
}
});
return;
}
for (name, owner) in FFI_REGISTRY {
if *name == symbol {
if provider_recording_permitted() {
Expand Down Expand Up @@ -1009,6 +1029,18 @@ mod tests {
);
}

#[test]
fn bun_serve_routes_to_http_and_fetch_providers() {
let _guard = ProviderTestGuard::new();
let _ = take_used_providers();
record_ffi_call("js_bun_serve");
let got = take_used_providers();
assert!(got.contains(&OwnerKind::WellKnown("http")));
assert!(got.contains(&OwnerKind::Stdlib {
feature: Some("web-fetch")
}));
}

/// #3954 regression: HTTP-suite native-table rows can emit newer
/// `perry-ext-http` or `perry-ext-net`
/// symbols without the module-import path being visible to collection.
Expand Down
11 changes: 11 additions & 0 deletions crates/perry-codegen/src/lower_call/native_table/bun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ use super::*;
/// `Bun.stdin` / `Bun.stdout` / `Bun.stderr` are property reads (handled by
/// `js_native_module_property_by_name`), not rows here.
pub(crate) const BUN_ROWS: &[NativeModSig] = &[
NativeModSig {
module: "bun",
has_receiver: false,
method: "serve",
class_filter: None,
// The listener lives in perry-ext-http and returns a native server
// handle, reusing the same event-loop pump as node:http.
runtime: "js_bun_serve",
args: &[NA_F64],
ret: NR_PTR,
},
NativeModSig {
module: "bun",
has_receiver: false,
Expand Down
Loading
Loading