diff --git a/cspell.config.js b/cspell.config.js index 0157c380..0db707ce 100644 --- a/cspell.config.js +++ b/cspell.config.js @@ -119,7 +119,8 @@ export default { 'shlv', 'shrv', 'sqrtv', - 'hypot' + 'hypot', + 'mdocumentation' ], ignorePaths: [ 'node_modules/**', diff --git a/docs/en/get-started/quick-usage.md b/docs/en/get-started/quick-usage.md index 4953e5c3..8e60cbd9 100644 --- a/docs/en/get-started/quick-usage.md +++ b/docs/en/get-started/quick-usage.md @@ -24,6 +24,53 @@ For Rust projects, configure `VmConfig` before creating the VM. ::: +## Diagnostic links + +Formatted VM errors include a `documentation:` metadata row by default. Its URL is derived from the error's `LVM` code and points to the matching page in the [error-code reference](/api-reference/error-codes/lvm001-code). + +Disable the row through the constructor configuration or the fluent API: + +::: code-group + +```rust [Native configuration] +let vm = LightVM::new(VmConfig { + error_options: Some(ErrorOptions { + diagnostic_links: false, + ..Default::default() + }), + ..Default::default() +}); + +let vm = LightVM::new(VmConfig::default()) + .with_diagnostic_links(false); +``` + +```ts [Node.js] +const vm = new LightVM({ + errorOptions: { + backtrace: false, + explain: false, + hint: true, + diagnosticLinks: false, + }, +}); + +vm.withDiagnosticLinks(false); +``` + +```ts [WASM] +const vm = new LightVM({ + caps: [], + errorOptions: { diagnosticLinks: false }, +}); + +vm.withDiagnosticLinks(false); +``` + +::: + +The same setting is retained by `tools()` and applies to errors from tool operations, including bytecode optimization. + ## Expected result You have a configured VM instance ready to load bytecode. Continue with the [Run Method](/api-reference/method-functions/run-method), or review [Capabilities](/api-reference/capabilities) before granting access. diff --git a/docs/examples/getStarted/builderPattern.ts b/docs/examples/getStarted/builderPattern.ts index 8712e874..c21e4bb8 100644 --- a/docs/examples/getStarted/builderPattern.ts +++ b/docs/examples/getStarted/builderPattern.ts @@ -14,6 +14,7 @@ const vm = new LightVM({ caps: [Capability.Observe, Capability.Control] }) .withNightly(false) // Allow nightly features (default: false) .withBacktrace(false) // Display backtrace details in error messages (default: false) .withExplain(false) // Display a more detailed hint in the error message (default: false) - .withHint(true); // Display a hint on error messages (default: true) + .withHint(true) // Display a hint on error messages (default: true) + .withDiagnosticLinks(false); // Hide links to error-code documentation (default: true) const tools = vm.tools(); diff --git a/docs/examples/getStarted/builder_pattern.rs b/docs/examples/getStarted/builder_pattern.rs index 31ef27d3..9ef97d0b 100644 --- a/docs/examples/getStarted/builder_pattern.rs +++ b/docs/examples/getStarted/builder_pattern.rs @@ -19,7 +19,8 @@ fn main() { .with_nightly(false) // Allow nightly features (default: false) .with_backtrace(false) // Display backtrace details in error messages (default: false) .with_explain(false) // Display a more detailed hint in the error message (default: false) - .with_hint(true); // Display a hint on error messages (default: true) + .with_hint(true) // Display a hint on error messages (default: true) + .with_diagnostic_links(false); // Hide links to error-code documentation (default: true) let tools = vm.tools(); -} \ No newline at end of file +} diff --git a/docs/examples/getStarted/objectPattern.ts b/docs/examples/getStarted/objectPattern.ts index 87c3a126..b7a3464a 100644 --- a/docs/examples/getStarted/objectPattern.ts +++ b/docs/examples/getStarted/objectPattern.ts @@ -9,6 +9,7 @@ const vm = new LightVM({ backtrace: false, // Display backtrace details in error messages (default: false) explain: false, // Display a more detailed hint in the error message (default: false) hint: true, // Display a hint on error messages (default: true) + diagnosticLinks: false, // Hide links to error-code documentation (default: true) }, securityConfig: { maxIo: 100, // Maximum number of I/O operations allowed (default: 100) diff --git a/docs/examples/getStarted/object_pattern.rs b/docs/examples/getStarted/object_pattern.rs index f9b291e4..523bf2e8 100644 --- a/docs/examples/getStarted/object_pattern.rs +++ b/docs/examples/getStarted/object_pattern.rs @@ -17,7 +17,8 @@ fn main() { error_options: Some(ErrorOptions { backtrace: false, // Display backtrace details in error messages (default: false) explain: false, // Display a more detailed hint in the error message (default: false) - hint: true // Display a hint on error messages (default: true) + hint: true, // Display a hint on error messages (default: true) + diagnostic_links: false // Hide links to error-code documentation (default: true) }), security_config: Some(SecurityConfig { max_io: 100, // Maximum number of I/O operations allowed (default: 100) @@ -34,4 +35,4 @@ fn main() { }); let tools = vm.tools(); -} \ No newline at end of file +} diff --git a/docs/id/get-started/quick-usage.md b/docs/id/get-started/quick-usage.md index 4cd0ae2d..8c6de527 100644 --- a/docs/id/get-started/quick-usage.md +++ b/docs/id/get-started/quick-usage.md @@ -24,6 +24,53 @@ Untuk proyek Rust, konfigurasikan `VmConfig` sebelum membuat VM. ::: +## Tautan diagnostik + +Error VM yang diformat menyertakan baris metadata `documentation:` secara default. URL-nya berasal dari kode `LVM` error dan mengarah ke halaman yang sesuai dalam [referensi kode error](/id/api-reference/error-codes/lvm001-code). + +Nonaktifkan baris tersebut melalui konfigurasi konstruktor atau API fluent: + +::: code-group + +```rust [Native configuration] +let vm = LightVM::new(VmConfig { + error_options: Some(ErrorOptions { + diagnostic_links: false, + ..Default::default() + }), + ..Default::default() +}); + +let vm = LightVM::new(VmConfig::default()) + .with_diagnostic_links(false); +``` + +```ts [Node.js] +const vm = new LightVM({ + errorOptions: { + backtrace: false, + explain: false, + hint: true, + diagnosticLinks: false, + }, +}); + +vm.withDiagnosticLinks(false); +``` + +```ts [WASM] +const vm = new LightVM({ + caps: [], + errorOptions: { diagnosticLinks: false }, +}); + +vm.withDiagnosticLinks(false); +``` + +::: + +Pengaturan yang sama dipertahankan oleh `tools()` dan berlaku untuk error dari operasi alat, termasuk optimasi bytecode. + ## Hasil yang diharapkan Anda memiliki instance VM terkonfigurasi yang siap memuat bytecode. Lanjutkan ke [Metode Run](/id/api-reference/method-functions/run-method), atau tinjau [Kapabilitas](/id/api-reference/capabilities) sebelum memberikan akses. diff --git a/rust/src/interfaces/interface.rs b/rust/src/interfaces/interface.rs index 2b06b0ff..7844c67d 100644 --- a/rust/src/interfaces/interface.rs +++ b/rust/src/interfaces/interface.rs @@ -74,6 +74,7 @@ pub struct LightVM { pub backtrace: bool, pub explain: bool, pub hint: bool, + pub diagnostic_links: bool, } impl LightVM { pub fn new_node( @@ -82,6 +83,7 @@ impl LightVM { backtrace: bool, explain: bool, hint: bool, + diagnostic_links: bool, ) -> Self { use crate::types::value::Value; use crate::types::vmstate::VmState; @@ -114,6 +116,7 @@ impl LightVM { backtrace, explain, hint, + diagnostic_links, } } #[inline(always)] @@ -126,8 +129,13 @@ impl LightVM { } Ok(()) } - pub fn set_mode(&self, backtrace: bool, explain: bool, hint: bool) { - crate::modules::vmerror::config::set_thread_error_config(backtrace, explain, hint); + pub fn set_mode(&self, backtrace: bool, explain: bool, hint: bool, diagnostic_links: bool) { + crate::modules::vmerror::config::set_thread_error_config( + backtrace, + explain, + hint, + diagnostic_links, + ); } pub fn index_metadata(&mut self) { self.functions.clear(); @@ -169,7 +177,12 @@ impl LightVM { get_versions() } pub fn load_internal(&mut self, source: String) -> Result<(), VMError> { - self.set_mode(self.backtrace, self.explain, self.hint); + self.set_mode( + self.backtrace, + self.explain, + self.hint, + self.diagnostic_links, + ); crate::modules::vmerror::get_backtrace::clear_backtrace(); if self.backtrace { crate::modules::vmerror::get_backtrace::capture_backtrace(); @@ -229,7 +242,12 @@ impl LightVM { Ok(()) } pub fn run_internal(&mut self, options: Option) -> Result { - self.set_mode(self.backtrace, self.explain, self.hint); + self.set_mode( + self.backtrace, + self.explain, + self.hint, + self.diagnostic_links, + ); crate::modules::vmerror::get_backtrace::clear_backtrace(); if self.backtrace { crate::modules::vmerror::get_backtrace::capture_backtrace(); @@ -280,7 +298,12 @@ impl LightVM { } #[inline] pub fn compile_internal(&mut self, config: CompileConfig) -> Result<(), VMError> { - self.set_mode(self.backtrace, self.explain, self.hint); + self.set_mode( + self.backtrace, + self.explain, + self.hint, + self.diagnostic_links, + ); crate::modules::vmerror::get_backtrace::clear_backtrace(); if self.backtrace { crate::modules::vmerror::get_backtrace::capture_backtrace(); @@ -498,7 +521,12 @@ impl LightVM { bytecode_raw: serde_json::Value, ) -> Result { self.require(Capability::Control)?; - self.set_mode(self.backtrace, self.explain, self.hint); + self.set_mode( + self.backtrace, + self.explain, + self.hint, + self.diagnostic_links, + ); crate::modules::vmerror::get_backtrace::clear_backtrace(); if self.backtrace { crate::modules::vmerror::get_backtrace::capture_backtrace(); @@ -642,6 +670,7 @@ mod tests { backtrace: false, explain: false, hint: true, + diagnostic_links: true, } } #[test] @@ -904,7 +933,7 @@ mod tests { vm.backtrace = true; vm.explain = true; vm.hint = false; - set_thread_error_config(false, false, true); + set_thread_error_config(false, false, true, true); let before = get_thread_or_global_config(); let result = vm.optimize_bytecode_internal(serde_json::json!([["noop"]])); let after = get_thread_or_global_config(); diff --git a/rust/src/interfaces/napi_interface.rs b/rust/src/interfaces/napi_interface.rs index a7b3092e..c005d6f2 100644 --- a/rust/src/interfaces/napi_interface.rs +++ b/rust/src/interfaces/napi_interface.rs @@ -103,6 +103,7 @@ impl NodeLightVM { backtrace: error_options.backtrace.unwrap_or(false), explain: error_options.explain.unwrap_or(false), hint: error_options.hint.unwrap_or(true), + diagnostic_links: error_options.diagnostic_links.unwrap_or(true), }, }) } @@ -187,6 +188,11 @@ impl NodeLightVM { self.inner.hint = enabled; Ok(()) } + #[napi(js_name = "withDiagnosticLinks")] + pub fn with_diagnostic_links(&mut self, enabled: bool) -> Result<()> { + self.inner.diagnostic_links = enabled; + Ok(()) + } #[napi] pub fn info(&mut self) -> Result { let info_vm = self.inner.info_internal(); @@ -561,6 +567,7 @@ impl NodeLightVM { is_backtrace, is_explain, is_hint, + self.inner.diagnostic_links, ); vm_instance.caps = self .inner @@ -599,6 +606,7 @@ impl NodeLightVM { #[cfg(test)] mod tests { use super::*; + use crate::types::js::js_error_options::JSErrorOptions; #[test] fn unknown_capability_uses_vm_error_display() { let config = VmNapiConfig { @@ -717,4 +725,22 @@ mod tests { r#"[["stop"]]"# ); } + #[test] + fn diagnostic_links_can_be_configured_and_updated() { + let mut vm = NodeLightVM::napi_new(VmNapiConfig { + error_options: Some(JSErrorOptions { + diagnostic_links: Some(false), + ..Default::default() + }), + ..Default::default() + }) + .expect("expected a VM"); + assert!(!vm.inner.diagnostic_links); + vm.with_diagnostic_links(true) + .expect("expected the setting to update"); + assert!(vm.inner.diagnostic_links); + vm.with_diagnostic_links(false) + .expect("expected the setting to update"); + assert!(!vm.inner.diagnostic_links); + } } diff --git a/rust/src/interfaces/native_interface.rs b/rust/src/interfaces/native_interface.rs index 4eca12d1..64554c71 100644 --- a/rust/src/interfaces/native_interface.rs +++ b/rust/src/interfaces/native_interface.rs @@ -225,6 +225,7 @@ impl LightVM { backtrace: error_options.backtrace, explain: error_options.explain, hint: error_options.hint, + diagnostic_links: error_options.diagnostic_links, } } pub fn set_max_io(mut self, value: usize) -> Self { @@ -283,6 +284,10 @@ impl LightVM { self.hint = enabled; self } + pub fn with_diagnostic_links(mut self, enabled: bool) -> Self { + self.diagnostic_links = enabled; + self + } #[cfg(not(feature = "wasm"))] pub fn info(&mut self) -> InfoVM { self.info_internal() @@ -464,6 +469,7 @@ impl LightVM { backtrace: self.backtrace, explain: self.explain, hint: self.hint, + diagnostic_links: self.diagnostic_links, time_budget: self.time_budget, can_control: self.caps.contains(&Capability::Control), can_debug: self.caps.contains(&Capability::Debug), @@ -475,6 +481,7 @@ pub struct LightVMTools { pub backtrace: bool, pub explain: bool, pub hint: bool, + pub diagnostic_links: bool, pub time_budget: TimeBudget, pub can_control: bool, pub can_debug: bool, @@ -534,6 +541,7 @@ impl LightVMTools { backtrace: self.backtrace, explain: self.explain, hint: self.hint, + diagnostic_links: self.diagnostic_links, }), }; let opt_str = LightVM::new(config) @@ -628,6 +636,14 @@ mod tests { assert_eq!(vm.state, VmState::Idle); } #[test] + fn diagnostic_links_can_be_disabled() { + let mut vm = LightVM::new(VmConfig::default()).with_diagnostic_links(false); + let error = vm + .load_internal("invalid source".to_string()) + .expect_err("expected invalid source to fail"); + assert!(!error.to_string().contains("documentation:")); + } + #[test] fn on_registers_listener() { let config = VmConfig { caps: vec![], @@ -818,6 +834,7 @@ mod tests { backtrace: tools.backtrace, explain: tools.explain, hint: tools.hint, + diagnostic_links: tools.diagnostic_links, }), ..Default::default() }); @@ -845,6 +862,7 @@ mod tests { backtrace: tools.backtrace, explain: tools.explain, hint: tools.hint, + diagnostic_links: tools.diagnostic_links, }), ..Default::default() }); diff --git a/rust/src/interfaces/wasm_interface.rs b/rust/src/interfaces/wasm_interface.rs index 60b71c55..82873b5e 100644 --- a/rust/src/interfaces/wasm_interface.rs +++ b/rust/src/interfaces/wasm_interface.rs @@ -102,6 +102,7 @@ impl WasmLightVM { backtrace: error_options.backtrace.unwrap_or(false), explain: error_options.explain.unwrap_or(false), hint: error_options.hint.unwrap_or(true), + diagnostic_links: error_options.diagnostic_links.unwrap_or(true), }, }) } @@ -173,6 +174,10 @@ impl WasmLightVM { pub fn with_hint(&mut self, enabled: bool) { self.inner.hint = enabled; } + #[wasm_bindgen(js_name = "withDiagnosticLinks")] + pub fn with_diagnostic_links(&mut self, enabled: bool) { + self.inner.diagnostic_links = enabled; + } #[wasm_bindgen] pub fn load(&mut self, source: String) -> Result<(), JsValue> { self @@ -384,6 +389,7 @@ impl WasmLightVM { backtrace: self.inner.backtrace, explain: self.inner.explain, hint: self.inner.hint, + diagnostic_links: self.inner.diagnostic_links, time_budget: self.inner.time_budget, can_observe: self.inner.caps.contains(&Capability::Observe), can_control: self.inner.caps.contains(&Capability::Control), @@ -398,6 +404,7 @@ pub struct WasmLightVMTools { pub backtrace: bool, pub explain: bool, pub hint: bool, + pub diagnostic_links: bool, time_budget: TimeBudget, pub can_observe: bool, pub can_control: bool, @@ -417,6 +424,7 @@ impl WasmLightVMTools { self.backtrace, self.explain, self.hint, + self.diagnostic_links, ); vm_instance.caps = { let mut caps = HashSet::new(); @@ -506,7 +514,7 @@ mod tests { use super::*; use crate::types::security_config::SecurityConfig; fn vm_with_control_capability() -> WasmLightVM { - let mut inner = LightVM::new_node(SecurityConfig::default(), false, false, false, true); + let mut inner = LightVM::new_node(SecurityConfig::default(), false, false, false, true, true); inner.caps.insert(Capability::Control); WasmLightVM { inner } } @@ -515,10 +523,14 @@ mod tests { let json_data = serde_json::json!({ "caps": [0, 2], "runtimeConfig": { "nightly": true }, - "errorOptions": { "hint": true } + "errorOptions": { "hint": true, "diagnosticLinks": false } }); let config: VmWasmConfig = serde_json::from_value(json_data).unwrap(); assert_eq!(config.caps, vec![0, 2]); + assert_eq!( + config.error_options.as_ref().unwrap().diagnostic_links, + Some(false) + ); #[cfg(target_arch = "wasm32")] { let mut vm = WasmLightVM::new(serde_wasm_bindgen::to_value(&config).unwrap()).unwrap(); @@ -530,6 +542,12 @@ mod tests { assert_eq!(config.runtime_config.unwrap().nightly, Some(true)); } #[test] + fn diagnostic_links_can_be_updated() { + let mut vm = vm_with_control_capability(); + vm.with_diagnostic_links(false); + assert!(!vm.inner.diagnostic_links); + } + #[test] fn start_and_finish_event_names_are_supported() { assert_eq!(parse_event("start"), Some(VmEvent::Start)); assert_eq!(parse_event("finish"), Some(VmEvent::Finish)); diff --git a/rust/src/modules/vmerror/config.rs b/rust/src/modules/vmerror/config.rs index 2e3b1f57..34dc2aca 100644 --- a/rust/src/modules/vmerror/config.rs +++ b/rust/src/modules/vmerror/config.rs @@ -15,6 +15,7 @@ pub struct VMErrorContainer { pub backtrace: bool, pub explain: bool, pub hint: bool, + pub diagnostic_links: bool, } impl Default for VMErrorContainer { fn default() -> Self { @@ -27,6 +28,7 @@ impl VMErrorContainer { backtrace: false, explain: false, hint: true, + diagnostic_links: true, } } pub fn get_value(&self) -> VMErrorContainer { @@ -34,6 +36,7 @@ impl VMErrorContainer { backtrace: self.backtrace, explain: self.explain, hint: self.hint, + diagnostic_links: self.diagnostic_links, } } } @@ -41,12 +44,13 @@ thread_local! { static THREAD_ERROR_CONFIG: RefCell> = const { RefCell::new(None) }; } static EXPLAIN_MODE: OnceLock> = OnceLock::new(); -pub fn set_thread_error_config(backtrace: bool, explain: bool, hint: bool) { +pub fn set_thread_error_config(backtrace: bool, explain: bool, hint: bool, diagnostic_links: bool) { THREAD_ERROR_CONFIG.with(|config| { *config.borrow_mut() = Some(VMErrorContainer { backtrace, explain, hint, + diagnostic_links, }); }); } diff --git a/rust/src/modules/vmerror/display.rs b/rust/src/modules/vmerror/display.rs index a1d9db54..2a1021fe 100644 --- a/rust/src/modules/vmerror/display.rs +++ b/rust/src/modules/vmerror/display.rs @@ -22,6 +22,7 @@ impl fmt::Display for VMError { let is_backtrace = config.backtrace; let is_explain = config.explain; let is_hint = config.hint; + let diagnostic_links = config.diagnostic_links; let err_type = match self { VMError::StackOverflow { .. } => "StackOverflow", VMError::StackUnderflow { .. } => "StackUnderflow", @@ -143,6 +144,13 @@ impl fmt::Display for VMError { write!(f, "\n {DARK_GRAY}error type: {}", err_type)?; } } + if diagnostic_links { + write!( + f, + "\n {RESET}{CYAN}├── {DARK_GRAY}documentation: {}{RESET}", + self.diagnostic_link() + )?; + } if is_backtrace { let backtrace = get_backtrace(); write!( diff --git a/rust/src/modules/vmerror/error.rs b/rust/src/modules/vmerror/error.rs index 24f0640f..8e2661fe 100644 --- a/rust/src/modules/vmerror/error.rs +++ b/rust/src/modules/vmerror/error.rs @@ -103,4 +103,124 @@ impl VMError { VMError::SystemError(_) => "LVM500", } } + /// Returns the documentation URL for this error. + #[cold] + pub fn diagnostic_link(&self) -> String { + format!( + "https://lightvm.vercel.app/api-reference/error-codes/{}-code", + self.error_code().to_ascii_lowercase() + ) + } +} +#[cfg(test)] +mod tests { + use super::VMError; + use crate::modules::vmerror::config::set_thread_error_config; + use smol_str::SmolStr; + fn all_errors() -> Vec { + vec![ + VMError::StackOverflow { ip: 1, limit: 2 }, + VMError::StackUnderflow { + ip: 1, + opcode: "POP", + }, + VMError::InvalidOpcode { + ip: 1, + code: SmolStr::new("INVALID"), + }, + VMError::TypeMismatch { + ip: 1, + expected: "number", + found: "string", + }, + VMError::SystemError(SmolStr::new("system failure")), + VMError::OutOfBounds { + ip: 1, + index: 2, + len: 1, + }, + VMError::InvalidJumpTarget { + ip: 1, + target: 2, + len: 1, + }, + VMError::FeatureRestricted { + ip: 1, + feature: "nightly", + }, + VMError::IoFlood { ip: 1 }, + VMError::ImportLimitReached { ip: 1 }, + VMError::UnauthorizedModule { + ip: 1, + module: SmolStr::new("module"), + }, + VMError::MemoryLimitExceeded { ip: 1 }, + VMError::CallLimitExceeded { ip: 1 }, + VMError::JumpLimitExceeded { ip: 1 }, + VMError::ExcessiveNopPadding, + VMError::InvalidMaxTicksConfig, + VMError::TickLimitExceeded, + ] + } + #[test] + fn every_error_has_a_diagnostic_link_containing_its_code() { + for error in all_errors() { + assert!( + error + .diagnostic_link() + .to_ascii_uppercase() + .contains(error.error_code()), + "diagnostic link missing {}", + error.error_code() + ); + } + } + #[test] + fn formatted_error_contains_its_diagnostic_link() { + set_thread_error_config(false, false, true, true); + let error = VMError::StackOverflow { ip: 1, limit: 2 }; + let formatted = error.to_string(); + let metadata_position = formatted.find("error type:").unwrap(); + let documentation_position = formatted.find("documentation:").unwrap(); + let hint_position = formatted.find("hint:").unwrap(); + assert!(formatted.contains(&format!("documentation: {}", error.diagnostic_link()))); + assert!(formatted.contains("\x1b[36m├── \x1b[2;37mdocumentation:")); + assert!(metadata_position < documentation_position); + assert!(documentation_position < hint_position); + } + #[test] + fn formatted_system_error_contains_its_diagnostic_link() { + set_thread_error_config(false, false, true, true); + let error = VMError::SystemError(SmolStr::new("system failure")); + let formatted = error.to_string(); + let error_position = formatted.find("system failure").unwrap(); + let documentation_position = formatted.find("documentation:").unwrap(); + let hint_position = formatted.find("hint:").unwrap(); + assert!(formatted.contains(&error.diagnostic_link())); + assert!(error_position < documentation_position); + assert!(documentation_position < hint_position); + } + #[test] + fn diagnostic_link_is_rendered_when_hints_are_disabled() { + set_thread_error_config(false, false, false, true); + let error = VMError::StackOverflow { ip: 1, limit: 2 }; + let formatted = error.to_string(); + assert!(formatted.contains(&error.diagnostic_link())); + assert!(formatted.contains("documentation:")); + } + #[test] + fn diagnostic_link_is_rendered_before_backtrace() { + set_thread_error_config(true, false, true, true); + let error = VMError::StackOverflow { ip: 1, limit: 2 }; + let formatted = error.to_string(); + let documentation_position = formatted.find("documentation:").unwrap(); + let backtrace_position = formatted.find("internal backtrace:").unwrap(); + assert!(documentation_position < backtrace_position); + } + #[test] + fn diagnostic_link_can_be_disabled() { + set_thread_error_config(false, false, true, false); + let error = VMError::StackOverflow { ip: 1, limit: 2 }; + assert!(!error.to_string().contains("documentation:")); + } } diff --git a/rust/src/types/error_options.rs b/rust/src/types/error_options.rs index 1445e34d..be4e6c7c 100644 --- a/rust/src/types/error_options.rs +++ b/rust/src/types/error_options.rs @@ -13,6 +13,7 @@ pub struct ErrorOptions { pub backtrace: bool, pub explain: bool, pub hint: bool, + pub diagnostic_links: bool, } impl Default for ErrorOptions { fn default() -> Self { @@ -20,6 +21,15 @@ impl Default for ErrorOptions { backtrace: false, explain: false, hint: true, + diagnostic_links: true, } } } +#[cfg(test)] +mod tests { + use super::ErrorOptions; + #[test] + fn diagnostic_links_are_enabled_by_default() { + assert!(ErrorOptions::default().diagnostic_links); + } +} diff --git a/rust/src/types/js/js_error_options.rs b/rust/src/types/js/js_error_options.rs index 52e7f919..28470507 100644 --- a/rust/src/types/js/js_error_options.rs +++ b/rust/src/types/js/js_error_options.rs @@ -18,4 +18,6 @@ pub struct JSErrorOptions { pub backtrace: Option, pub explain: Option, pub hint: Option, + #[ts(rename = "diagnosticLinks")] + pub diagnostic_links: Option, } diff --git a/rust/src/types/wasm/wasm_error_options.rs b/rust/src/types/wasm/wasm_error_options.rs index 031f8044..74dd03b1 100644 --- a/rust/src/types/wasm/wasm_error_options.rs +++ b/rust/src/types/wasm/wasm_error_options.rs @@ -12,8 +12,10 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] #[derive(Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct WASMErrorOptions { pub backtrace: Option, pub explain: Option, pub hint: Option, + pub diagnostic_links: Option, } diff --git a/ts/src/generated/CompileConfig.ts b/ts/src/generated/CompileConfig.ts index 629980a1..cb564829 100644 --- a/ts/src/generated/CompileConfig.ts +++ b/ts/src/generated/CompileConfig.ts @@ -1,7 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type CompileConfig = { - targetArch: number; - fileType: number; - path: string; -}; +export type CompileConfig = { targetArch: number, fileType: number, path: string, }; diff --git a/ts/src/generated/ErrorOptions.ts b/ts/src/generated/ErrorOptions.ts index 491974ad..62222755 100644 --- a/ts/src/generated/ErrorOptions.ts +++ b/ts/src/generated/ErrorOptions.ts @@ -4,4 +4,5 @@ export type ErrorOptions = { backtrace: boolean | null; explain: boolean | null; hint: boolean | null; + diagnosticLinks: boolean | null; }; diff --git a/ts/src/generated/Instructions.ts b/ts/src/generated/Instructions.ts index a3d796f8..71228a68 100644 --- a/ts/src/generated/Instructions.ts +++ b/ts/src/generated/Instructions.ts @@ -2,134 +2,4 @@ import type { PrimitiveTypes } from './PrimitiveTypes.js'; import type { Value } from './Value.js'; -export type Instructions = - | { push_int16: number } - | { push_int32: number } - | { push_int64: number } - | { push_int128: number } - | { push_float16: number } - | { push_float32: number } - | { push_float64: number } - | { push_string: string } - | { push_array: any[] } - | { push_object: Record } - | { push_bool: boolean } - | 'push_null' - | 'push_undefined' - | 'push_na_n' - | { push: Value } - | { val: string } - | { val_idx: number } - | { set: string } - | { set_idx: number } - | { get: string } - | { get_idx: number } - | { add: PrimitiveTypes } - | { addv: PrimitiveTypes } - | { sub: PrimitiveTypes } - | { subv: PrimitiveTypes } - | { mul: PrimitiveTypes } - | { mulv: PrimitiveTypes } - | { div: PrimitiveTypes } - | { divv: PrimitiveTypes } - | { mod: PrimitiveTypes } - | { modv: PrimitiveTypes } - | { shl: PrimitiveTypes } - | { shr: PrimitiveTypes } - | { ror: PrimitiveTypes } - | { rol: PrimitiveTypes } - | { sin: PrimitiveTypes } - | { cos: PrimitiveTypes } - | { tan: PrimitiveTypes } - | { sinv: PrimitiveTypes } - | { cosv: PrimitiveTypes } - | { tanv: PrimitiveTypes } - | { asin: PrimitiveTypes } - | { acos: PrimitiveTypes } - | { atan: PrimitiveTypes } - | { atan2: PrimitiveTypes } - | { asinv: PrimitiveTypes } - | { acosv: PrimitiveTypes } - | { atanv: PrimitiveTypes } - | { atan2v: PrimitiveTypes } - | { sinh: PrimitiveTypes } - | { cosh: PrimitiveTypes } - | { tanh: PrimitiveTypes } - | { sinhv: PrimitiveTypes } - | { coshv: PrimitiveTypes } - | { tanhv: PrimitiveTypes } - | { asinh: PrimitiveTypes } - | { acosh: PrimitiveTypes } - | { atanh: PrimitiveTypes } - | { asinhv: PrimitiveTypes } - | { acoshv: PrimitiveTypes } - | { atanhv: PrimitiveTypes } - | { sqrt: PrimitiveTypes } - | { cbrt: PrimitiveTypes } - | { neg: PrimitiveTypes } - | { negv: PrimitiveTypes } - | { ln: PrimitiveTypes } - | { exp: PrimitiveTypes } - | { log2: PrimitiveTypes } - | { log10: PrimitiveTypes } - | { pow: PrimitiveTypes } - | { powi: PrimitiveTypes } - | { powf: PrimitiveTypes } - | { powv: PrimitiveTypes } - | { powiv: PrimitiveTypes } - | { powfv: PrimitiveTypes } - | { gt: PrimitiveTypes } - | { lt: PrimitiveTypes } - | { ge: PrimitiveTypes } - | { le: PrimitiveTypes } - | { eq: PrimitiveTypes } - | { neq: PrimitiveTypes } - | { dot: PrimitiveTypes } - | { cross: PrimitiveTypes } - | 'and' - | 'or' - | 'xor' - | 'not' - | 'print' - | 'println' - | 'stdout' - | 'stdoutln' - | 'stdin' - | 'clear_screen' - | { if_false: number } - | { jump: number } - | { inc: [string, PrimitiveTypes] } - | { inc_idx: [number, PrimitiveTypes] } - | { dec: [string, PrimitiveTypes] } - | { dec_idx: [number, PrimitiveTypes] } - | { call: [string, PrimitiveTypes] } - | { func: [string, number, number, number, string[]] } - | 'stop' - | 'return' - | { break: number } - | { access: string } - | 'access_index' - | 'to_string' - | 'to_short' - | 'to_integer' - | 'to_long' - | 'to_octa' - | 'to_half' - | 'to_float' - | 'to_double' - | { make_obj: number } - | { make_array: number } - | 'type_of' - | 'inspect_obj' - | 'inspect_arr' - | 'length' - | 'concat' - | 'dup' - | 'swap' - | { set_prop: string } - | { import: [string, number] } - | { export: string } - | { instantiate: [string, number] } - | 'nop' - | 'truncate' - | 'shrink'; +export type Instructions = { "push_int16": number } | { "push_int32": number } | { "push_int64": number } | { "push_int128": number } | { "push_float16": number } | { "push_float32": number } | { "push_float64": number } | { "push_string": string } | { "push_array": any[] } | { "push_object": Record } | { "push_bool": boolean } | "push_null" | "push_undefined" | "push_na_n" | { "push": Value } | { "val": string } | { "val_idx": number } | { "set": string } | { "set_idx": number } | { "get": string } | { "get_idx": number } | { "add": PrimitiveTypes } | { "addv": PrimitiveTypes } | { "sub": PrimitiveTypes } | { "subv": PrimitiveTypes } | { "mul": PrimitiveTypes } | { "mulv": PrimitiveTypes } | { "div": PrimitiveTypes } | { "divv": PrimitiveTypes } | { "mod": PrimitiveTypes } | { "modv": PrimitiveTypes } | { "shl": PrimitiveTypes } | { "shlv": PrimitiveTypes } | { "shr": PrimitiveTypes } | { "shrv": PrimitiveTypes } | { "ror": PrimitiveTypes } | { "rorv": PrimitiveTypes } | { "rol": PrimitiveTypes } | { "rolv": PrimitiveTypes } | { "sin": PrimitiveTypes } | { "cos": PrimitiveTypes } | { "tan": PrimitiveTypes } | { "sinv": PrimitiveTypes } | { "cosv": PrimitiveTypes } | { "tanv": PrimitiveTypes } | { "asin": PrimitiveTypes } | { "acos": PrimitiveTypes } | { "atan": PrimitiveTypes } | { "atan2": PrimitiveTypes } | { "asinv": PrimitiveTypes } | { "acosv": PrimitiveTypes } | { "atanv": PrimitiveTypes } | { "atan2v": PrimitiveTypes } | { "sinh": PrimitiveTypes } | { "cosh": PrimitiveTypes } | { "tanh": PrimitiveTypes } | { "sinhv": PrimitiveTypes } | { "coshv": PrimitiveTypes } | { "tanhv": PrimitiveTypes } | { "asinh": PrimitiveTypes } | { "acosh": PrimitiveTypes } | { "atanh": PrimitiveTypes } | { "asinhv": PrimitiveTypes } | { "acoshv": PrimitiveTypes } | { "atanhv": PrimitiveTypes } | { "sqrt": PrimitiveTypes } | { "sqrtv": PrimitiveTypes } | { "cbrt": PrimitiveTypes } | { "cbrtv": PrimitiveTypes } | { "neg": PrimitiveTypes } | { "negv": PrimitiveTypes } | { "ln": PrimitiveTypes } | { "lnv": PrimitiveTypes } | { "exp": PrimitiveTypes } | { "expv": PrimitiveTypes } | { "log2": PrimitiveTypes } | { "log2v": PrimitiveTypes } | { "log10": PrimitiveTypes } | { "log10v": PrimitiveTypes } | { "pow": PrimitiveTypes } | { "powi": PrimitiveTypes } | { "powf": PrimitiveTypes } | { "powv": PrimitiveTypes } | { "powiv": PrimitiveTypes } | { "powfv": PrimitiveTypes } | { "gt": PrimitiveTypes } | { "lt": PrimitiveTypes } | { "ge": PrimitiveTypes } | { "le": PrimitiveTypes } | { "eq": PrimitiveTypes } | { "neq": PrimitiveTypes } | { "dot": PrimitiveTypes } | { "cross": PrimitiveTypes } | { "normalize": PrimitiveTypes } | "and" | "or" | "xor" | "not" | "print" | "println" | "stdout" | "stdoutln" | "stdin" | "clear_screen" | { "if_false": number } | { "jump": number } | { "inc": [string, PrimitiveTypes] } | { "inc_idx": [number, PrimitiveTypes] } | { "dec": [string, PrimitiveTypes] } | { "dec_idx": [number, PrimitiveTypes] } | { "call": [string, PrimitiveTypes] } | { "func": [string, number, number, number, string[]] } | "stop" | "return" | { "break": number } | { "access": string } | "access_index" | "to_string" | "to_short" | "to_integer" | "to_long" | "to_octa" | "to_half" | "to_float" | "to_double" | { "make_obj": number } | { "make_array": number } | "type_of" | "inspect_obj" | "inspect_arr" | "length" | "concat" | "dup" | "swap" | { "set_prop": string } | { "import": [string, number] } | { "export": string } | { "instantiate": [string, number] } | "nop" | "truncate" | "shrink"; diff --git a/ts/src/generated/PrimitiveTypes.ts b/ts/src/generated/PrimitiveTypes.ts index cbeec870..764899b2 100644 --- a/ts/src/generated/PrimitiveTypes.ts +++ b/ts/src/generated/PrimitiveTypes.ts @@ -1,4 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type PrimitiveTypes = - 'Sht' | 'Int' | 'Lng' | 'Oct' | 'Hlf' | 'Flt' | 'Dbl' | 'Str'; +export type PrimitiveTypes = "Sht" | "Int" | "Lng" | "Oct" | "Hlf" | "Flt" | "Dbl" | "Str"; diff --git a/ts/src/generated/Value.ts b/ts/src/generated/Value.ts index ca621643..c665ae83 100644 --- a/ts/src/generated/Value.ts +++ b/ts/src/generated/Value.ts @@ -1,18 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type Value = - | number - | number - | number - | number - | number - | number - | number - | string - | any[] - | Record - | boolean - | null - | null - | null - | string; +export type Value = number | number | number | number | number | number | number | string | any[] | Record | boolean | null | null | null | string; diff --git a/ts/src/index.ts b/ts/src/index.ts index b1918cbb..3f0a5db2 100644 --- a/ts/src/index.ts +++ b/ts/src/index.ts @@ -57,7 +57,12 @@ export class LightVM { private static readonly DEFAULTS: VMConfig = { caps: [Capability.Observe], runtimeConfig: { nightly: false }, - errorOptions: { backtrace: false, explain: false, hint: true }, + errorOptions: { + backtrace: false, + explain: false, + hint: true, + diagnosticLinks: true, + }, securityConfig: { maxIo: 100, maxImport: 3, @@ -164,6 +169,8 @@ export class LightVM { withExplain = (en: boolean) => this.updateConfig('errorOptions', 'explain', en); withHint = (en: boolean) => this.updateConfig('errorOptions', 'hint', en); + withDiagnosticLinks = (en: boolean) => + this.updateConfig('errorOptions', 'diagnosticLinks', en); info() { return formatInfoVM(this.wrap(() => this.instance.info())); diff --git a/types/generated/ErrorOptions.d.ts b/types/generated/ErrorOptions.d.ts index 0a236f38..c4ca04a6 100644 --- a/types/generated/ErrorOptions.d.ts +++ b/types/generated/ErrorOptions.d.ts @@ -2,4 +2,5 @@ export type ErrorOptions = { backtrace: boolean | null; explain: boolean | null; hint: boolean | null; + diagnosticLinks: boolean | null; }; diff --git a/types/generated/Instructions.d.ts b/types/generated/Instructions.d.ts index c8c1b6fb..3fe32a11 100644 --- a/types/generated/Instructions.d.ts +++ b/types/generated/Instructions.d.ts @@ -58,12 +58,20 @@ export type Instructions = { "modv": PrimitiveTypes; } | { "shl": PrimitiveTypes; +} | { + "shlv": PrimitiveTypes; } | { "shr": PrimitiveTypes; +} | { + "shrv": PrimitiveTypes; } | { "ror": PrimitiveTypes; +} | { + "rorv": PrimitiveTypes; } | { "rol": PrimitiveTypes; +} | { + "rolv": PrimitiveTypes; } | { "sin": PrimitiveTypes; } | { @@ -118,20 +126,32 @@ export type Instructions = { "atanhv": PrimitiveTypes; } | { "sqrt": PrimitiveTypes; +} | { + "sqrtv": PrimitiveTypes; } | { "cbrt": PrimitiveTypes; +} | { + "cbrtv": PrimitiveTypes; } | { "neg": PrimitiveTypes; } | { "negv": PrimitiveTypes; } | { "ln": PrimitiveTypes; +} | { + "lnv": PrimitiveTypes; } | { "exp": PrimitiveTypes; +} | { + "expv": PrimitiveTypes; } | { "log2": PrimitiveTypes; +} | { + "log2v": PrimitiveTypes; } | { "log10": PrimitiveTypes; +} | { + "log10v": PrimitiveTypes; } | { "pow": PrimitiveTypes; } | { @@ -160,6 +180,8 @@ export type Instructions = { "dot": PrimitiveTypes; } | { "cross": PrimitiveTypes; +} | { + "normalize": PrimitiveTypes; } | "and" | "or" | "xor" | "not" | "print" | "println" | "stdout" | "stdoutln" | "stdin" | "clear_screen" | { "if_false": number; } | { diff --git a/types/index.d.ts b/types/index.d.ts index 52ca9319..5864adba 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -72,6 +72,7 @@ export declare class LightVM { withBacktrace: (en: boolean) => this; withExplain: (en: boolean) => this; withHint: (en: boolean) => this; + withDiagnosticLinks: (en: boolean) => this; info(): string; load(source: Instructions[] | string): this; run(options?: any): any; diff --git a/types/utils/isMusl.d.ts b/types/utils/isMusl.d.ts index 9ede6fb5..3156968c 100644 --- a/types/utils/isMusl.d.ts +++ b/types/utils/isMusl.d.ts @@ -7,4 +7,4 @@ * * http://www.apache.org/licenses/LICENSE-2.0 */ -export declare function isMusl(reportProvider?: NodeJS.ProcessReport): boolean; +export declare function isMusl(reportProvider?: any): any;