diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 7d494c88ef..53f81d3d12 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -34,6 +34,7 @@ use deno_permissions::PermissionCheckError; pub use node_resolver::DENO_SUPPORTED_BUILTIN_NODE_MODULES as SUPPORTED_BUILTIN_NODE_MODULES; pub use node_resolver::PathClean; use ops::handle_wrap::AsyncId; +pub use ops::inspector::InspectorServerUrl; pub use ops::ipc::ChildPipeFd; use ops::vm; pub use ops::vm::ContextInitMode; diff --git a/ext/node/ops/inspector.rs b/ext/node/ops/inspector.rs index 03f95e319e..8ad986075d 100644 --- a/ext/node/ops/inspector.rs +++ b/ext/node/ops/inspector.rs @@ -13,6 +13,8 @@ use deno_core::v8; use deno_error::JsErrorBox; use deno_permissions::PermissionsContainer; +pub struct InspectorServerUrl(pub String); + #[op2(fast)] pub fn op_inspector_enabled() -> bool { // TODO: hook up to InspectorServer @@ -49,9 +51,18 @@ pub fn op_inspector_close() { #[op2] #[string] -pub fn op_inspector_url() -> Option { - // TODO: hook up to InspectorServer - None +pub fn op_inspector_url( + state: &mut OpState, +) -> Result, InspectorConnectError> { + state + .borrow_mut::() + .check_sys("inspector", "inspector.url")?; + + Ok( + state + .try_borrow::() + .map(|url| url.0.to_string()), + ) } #[op2(fast)] diff --git a/ext/node/polyfills/inspector.js b/ext/node/polyfills/inspector.js index 7ea341e214..8029c1403b 100644 --- a/ext/node/polyfills/inspector.js +++ b/ext/node/polyfills/inspector.js @@ -203,7 +203,11 @@ function close() { } function url() { - return op_inspector_url(); + const u = op_inspector_url(); + if (u === null) { + return undefined; + } + return u; } function waitForDebugger() { diff --git a/runtime/inspector_server.rs b/runtime/inspector_server.rs index 862a4badc5..b07bd42290 100644 --- a/runtime/inspector_server.rs +++ b/runtime/inspector_server.rs @@ -30,6 +30,7 @@ use deno_core::serde_json::Value; use deno_core::serde_json::json; use deno_core::unsync::spawn; use deno_core::url::Url; +use deno_node::InspectorServerUrl; use fastwebsockets::Frame; use fastwebsockets::OpCode; use fastwebsockets::WebSocket; @@ -104,7 +105,7 @@ impl InspectorServer { module_url: String, inspector: Rc, wait_for_session: bool, - ) { + ) -> InspectorServerUrl { let session_sender = inspector.get_session_sender(); let deregister_rx = inspector.add_deregister_handler(); let info = InspectorInfo::new( @@ -114,7 +115,11 @@ impl InspectorServer { module_url, wait_for_session, ); + let url = InspectorServerUrl( + info.get_websocket_debugger_url(&self.host.to_string()), + ); self.register_inspector_tx.unbounded_send(info).unwrap(); + url } } diff --git a/runtime/worker.rs b/runtime/worker.rs index 1339d3fb31..cf5429e11b 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -619,12 +619,13 @@ impl MainWorker { } if let Some(server) = options.maybe_inspector_server.clone() { - server.register_inspector( + let inspector_url = server.register_inspector( main_module.to_string(), js_runtime.inspector(), options.should_break_on_first_statement || options.should_wait_for_inspector_session, ); + js_runtime.op_state().borrow_mut().put(inspector_url); } let ( diff --git a/tests/integration/inspector_tests.rs b/tests/integration/inspector_tests.rs index 0b8575eb4f..0b1c48e29a 100644 --- a/tests/integration/inspector_tests.rs +++ b/tests/integration/inspector_tests.rs @@ -1877,3 +1877,29 @@ async fn inspector_node_worker_enable() { tester.child.kill().unwrap(); tester.child.wait().unwrap(); } + +#[test(flaky)] +fn inspector_node_runtime_api_url() { + let script = util::testdata_path().join("inspector/node/url.js"); + let child = util::deno_cmd() + .arg("run") + .arg("--allow-sys") + .arg(inspect_flag_with_unique_port("--inspect")) + .arg(script) + .piped_output() + .spawn() + .unwrap(); + + let output = child.wait_with_output().unwrap(); + let stderr = String::from_utf8(output.stderr).unwrap(); + let first_line = stderr + .lines() + .collect::>() + .first() + .unwrap() + .to_string(); + let expected_url = first_line.strip_prefix("Debugger listening on ").unwrap(); + let stdout = String::from_utf8(output.stdout).unwrap(); + let actual_url = stdout.trim(); + assert_eq!(actual_url, expected_url); +} diff --git a/tests/testdata/inspector/node/url.js b/tests/testdata/inspector/node/url.js new file mode 100644 index 0000000000..9b39635f11 --- /dev/null +++ b/tests/testdata/inspector/node/url.js @@ -0,0 +1,2 @@ +import inspector from "node:inspector"; +console.log(inspector.url()); diff --git a/tests/unit_node/inspector_test.ts b/tests/unit_node/inspector_test.ts index 9a7922b575..769a0479cd 100644 --- a/tests/unit_node/inspector_test.ts +++ b/tests/unit_node/inspector_test.ts @@ -3,7 +3,7 @@ import inspector, { Session } from "node:inspector"; import inspectorPromises, { Session as SessionPromise, } from "node:inspector/promises"; -import { assertEquals } from "@std/assert"; +import { assertEquals, assertThrows } from "@std/assert"; Deno.test("[node/inspector] - importing inspector works", () => { assertEquals(typeof inspector.open, "function"); @@ -94,3 +94,16 @@ Deno.test({ ]); }, }); + +Deno.test("[node/inspector] - url() requires sys permission", { + permissions: { sys: false }, +}, () => { + assertThrows(() => inspector.url(), Deno.errors.NotCapable); +}); + +Deno.test("[node/inspector] - url() returns undefined when no --inspect flag", { + permissions: { sys: true }, +}, () => { + const url = inspector.url(); + assertEquals(url, undefined); +});