Skip to content
Merged
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
44 changes: 44 additions & 0 deletions crates/perry-hir/src/lower/expr_member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,16 @@ pub(super) fn lower_member(ctx: &mut LoweringContext, member: &ast::MemberExpr)
("headersUrl".to_string(), Expr::String(String::new())),
]));
}
// #1378: process.features — object of boolean capability
// flags. Consumers feature-detect on individual fields
// (e.g. `process.features.openssl_is_boringssl`); a bare
// read of `process.features` previously returned a 0
// sentinel, so `.X` on it was always undefined. Lower
// to an inline object literal matching the Node shape.
// All Perry flags are `false` except `ipv6` (the
// runtime's `node:dgram`/network stack handles it) —
// the literal mirrors what we actually link in.
"features" => return Ok(process_features_literal()),
_ => {}
}
}
Expand Down Expand Up @@ -219,6 +229,7 @@ pub(super) fn lower_member(ctx: &mut LoweringContext, member: &ast::MemberExpr)
("headersUrl".to_string(), Expr::String(String::new())),
]));
}
"features" => return Ok(process_features_literal()),
_ => {}
}
}
Expand Down Expand Up @@ -1187,3 +1198,36 @@ fn is_stream_api_member(module: &str, prop: &str) -> bool {
_ => false,
}
}

/// #1378: `process.features` literal. Boolean capability flags Node
/// exposes so libraries can detect what the runtime links in. Perry
/// links its own networking/TLS stack; the values here reflect what
/// the runtime *actually* supports, not what Node would say — readers
/// generally branch on `openssl_is_boringssl` / `quic` / `typescript`
/// rather than rejecting any unrecognised value, so a Perry-honest
/// shape is safer than parroting Node's.
fn process_features_literal() -> Expr {
fn b(k: &str, v: bool) -> (String, Expr) {
(k.to_string(), Expr::Bool(v))
}
Expr::Object(vec![
b("inspector", false),
b("debug", false),
b("uv", false),
b("ipv6", true),
b("tls_alpn", true),
b("tls_sni", true),
b("tls_ocsp", true),
b("tls", true),
b("openssl_is_boringssl", false),
b("cached_builtins", false),
b("require_module", false),
b("quic", false),
// Perry compiles TypeScript natively (AOT) — surface as
// `"transform"` to distinguish from Node's `"strip"` mode.
(
"typescript".to_string(),
Expr::String("transform".to_string()),
),
])
}
15 changes: 15 additions & 0 deletions test-parity/node-suite/process/features/features.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// process.features — object of boolean capability flags. Consumers
// branch on individual fields (TLS variant, openssl/boringssl,
// IPv6 availability). Regression cover for #1378 (Perry was
// returning a 0 sentinel, so any field read was undefined).
//
// Cross-runtime parity is checked on _shape_ rather than the specific
// flag values: Perry's TLS/IPv6/typescript story doesn't have to match
// Node bit-for-bit, only the field types.
const f = process.features;
console.log("typeof:", typeof f);
console.log("tls type:", typeof f.tls);
console.log("ipv6 type:", typeof f.ipv6);
console.log("openssl_is_boringssl type:", typeof f.openssl_is_boringssl);
console.log("tls_alpn type:", typeof f.tls_alpn);
console.log("inspector type:", typeof f.inspector);