From d242cb9d702794213393651a4fe938bbbfcc1ea7 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Thu, 16 Jun 2022 17:03:43 -0400 Subject: [PATCH] Document, improve some API functionality --- Cargo.lock | 28 +++++++++--------- README.md | 14 +++++++-- README.tpl | 7 +++++ src/main.rs | 10 +++++-- usdpl-back/Cargo.toml | 9 +++--- usdpl-back/README.md | 6 ++-- usdpl-back/README.tpl | 5 ++++ usdpl-back/src/callable.rs | 8 +++++ usdpl-back/src/instance.rs | 29 +++++++++++++++---- usdpl-back/src/lib.rs | 4 ++- usdpl-core/Cargo.toml | 5 +++- usdpl-core/README.md | 4 +-- usdpl-core/README.tpl | 5 ++++ usdpl-core/src/api_common/target.rs | 6 ++++ usdpl-core/src/lib.rs | 4 +++ usdpl-core/src/remote_call.rs | 5 ++++ usdpl-core/src/serdes/primitive.rs | 11 +++++++ usdpl-core/src/serdes/traits.rs | 9 ++++++ usdpl-core/src/socket.rs | 15 +++++++++- usdpl-front/Cargo.toml | 19 +++++------- usdpl-front/README.md | 9 ++++++ usdpl-front/README.tpl | 5 ++++ usdpl-front/scripts/generate_embedded_wasm.py | 10 +++---- usdpl-front/src/imports.rs | 3 ++ usdpl-front/src/lib.rs | 9 ++++-- 25 files changed, 183 insertions(+), 56 deletions(-) create mode 100644 README.tpl create mode 100644 usdpl-back/README.tpl create mode 100644 usdpl-core/README.tpl create mode 100644 usdpl-front/README.tpl diff --git a/Cargo.lock b/Cargo.lock index a6ccd7b..e679314 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -924,20 +924,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "usdpl" -version = "0.4.0" -dependencies = [ - "console_error_panic_hook", - "js-sys", - "usdpl-core", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test", - "web-sys", - "wee_alloc", -] - [[package]] name = "usdpl-back" version = "0.4.0" @@ -955,6 +941,20 @@ dependencies = [ "base64", ] +[[package]] +name = "usdpl-front" +version = "0.4.0" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "usdpl-core", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", + "wee_alloc", +] + [[package]] name = "usdpl-rs" version = "0.1.0" diff --git a/README.md b/README.md index 7eef3a9..5c9f297 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +[![usdpl-back](https://img.shields.io/crates/v/usdpl-back?label=usdpl-back&style=flat-square)](https://crates.io/crates/usdpl-back) +[![usdpl-core](https://img.shields.io/crates/v/usdpl-core?label=usdpl-core&style=flat-square)](https://crates.io/crates/usdpl-core) +[![usdpl-front](https://img.shields.io/crates/v/usdpl-front?label=usdpl-front&style=flat-square)](https://crates.io/crates/usdpl-front) + # usdpl-rs Universal Steam Deck Plugin Library @@ -5,11 +9,15 @@ Universal Steam Deck Plugin Library A faster, lighter way to write plugins ### Goals -- [ ] Minimum viable plugin -- [ ] Call back-end API from front-end UI -- [ ] Async support +- [x] Minimum viable plugin +- [x] Call back-end API from front-end UI +- [x] External API documentation +- [ ] Internal API documentation +- [x] Async support +- [ ] Sync support - [ ] PluginLoader/Decky support - [ ] Crankshaft support - [ ] Unnamed plugin system support - [ ] Cross-framework tooling +- [ ] Other programming languages support (C bindings) diff --git a/README.tpl b/README.tpl new file mode 100644 index 0000000..6e310cb --- /dev/null +++ b/README.tpl @@ -0,0 +1,7 @@ +[![usdpl-back](https://img.shields.io/crates/v/usdpl-back?label=usdpl-back&style=flat-square)](https://crates.io/crates/usdpl-back) +[![usdpl-core](https://img.shields.io/crates/v/usdpl-core?label=usdpl-core&style=flat-square)](https://crates.io/crates/usdpl-core) +[![usdpl-front](https://img.shields.io/crates/v/usdpl-front?label=usdpl-front&style=flat-square)](https://crates.io/crates/usdpl-front) + +# {{crate}} + +{{readme}} diff --git a/src/main.rs b/src/main.rs index a60daf1..15d1313 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,13 +3,17 @@ //! A faster, lighter way to write plugins //! //! ## Goals -//! - [ ] Minimum viable plugin -//! - [ ] Call back-end API from front-end UI -//! - [ ] Async support +//! - [x] Minimum viable plugin +//! - [x] Call back-end API from front-end UI +//! - [x] External API documentation +//! - [ ] Internal API documentation +//! - [x] Async support +//! - [ ] Sync support //! - [ ] PluginLoader/Decky support //! - [ ] Crankshaft support //! - [ ] Unnamed plugin system support //! - [ ] Cross-framework tooling +//! - [ ] Other programming languages support (C bindings) //! fn main() { println!("Hello, USDPL!"); diff --git a/usdpl-back/Cargo.toml b/usdpl-back/Cargo.toml index 47a9084..1b82465 100644 --- a/usdpl-back/Cargo.toml +++ b/usdpl-back/Cargo.toml @@ -8,9 +8,10 @@ readme = "README.md" description = "Universal Steam Deck Plugin Library back-end" [features] -default = [] -decky = [] -crankshaft = [] +default = ["blocking"] +decky = ["usdpl-core/decky"] +crankshaft = ["usdpl-core/crankshaft"] +blocking = ["tokio"] # synchronous API for async functionality, using tokio [dependencies] usdpl-core = { version = "0.4.0", path = "../usdpl-core" } @@ -18,4 +19,4 @@ usdpl-core = { version = "0.4.0", path = "../usdpl-core" } # HTTP web framework warp = { version = "0.3" } bytes = { version = "1.1" } -tokio = { version = "1.19", features = ["rt", "rt-multi-thread"] } +tokio = { version = "1.19", features = ["rt", "rt-multi-thread"], optional = true } diff --git a/usdpl-back/README.md b/usdpl-back/README.md index b83b4d4..73cdd4c 100644 --- a/usdpl-back/README.md +++ b/usdpl-back/README.md @@ -1,9 +1,9 @@ +[![Crates.io](https://img.shields.io/crates/v/usdpl-back?style=flat-square)](https://crates.io/crates/usdpl-back) + # usdpl-back Back-end library for plugins. Targets x86_64 (native Steam Deck ISA). -This is a minimalist TCP server for handling events from the front-end. +This is a minimalist web server for handling events from the front-end. - -License: GPL-3.0-only diff --git a/usdpl-back/README.tpl b/usdpl-back/README.tpl new file mode 100644 index 0000000..ea0e67b --- /dev/null +++ b/usdpl-back/README.tpl @@ -0,0 +1,5 @@ +[![Crates.io](https://img.shields.io/crates/v/usdpl-back?style=flat-square)](https://crates.io/crates/usdpl-back) + +# {{crate}} + +{{readme}} diff --git a/usdpl-back/src/callable.rs b/usdpl-back/src/callable.rs index 1bee16c..a3fdd73 100644 --- a/usdpl-back/src/callable.rs +++ b/usdpl-back/src/callable.rs @@ -1,5 +1,13 @@ use usdpl_core::serdes::Primitive; +/// A function which can be called from the front-end (remotely) pub trait Callable: Send + Sync { + /// Invoke the function fn call(&mut self, params: Vec) -> Vec; } + +impl) -> Vec) + Send + Sync> Callable for F { + fn call(&mut self, params: Vec) -> Vec { + (self)(params) + } +} diff --git a/usdpl-back/src/instance.rs b/usdpl-back/src/instance.rs index 4799a30..202df6f 100644 --- a/usdpl-back/src/instance.rs +++ b/usdpl-back/src/instance.rs @@ -27,19 +27,31 @@ impl Instance { } } - /// Register a function which can be invoked by the front-end + /// Register a function which can be invoked by the front-end, builder style pub fn register, F: Callable + 'static>( - &mut self, + mut self, name: S, f: F, - ) -> &mut Self { - //CALLS.lock().unwrap().insert(name.into(), Mutex::new(Box::new(f))); + ) -> Self { self.calls .insert(name.into(), Arc::new(Mutex::new(Box::new(f)))); self } - pub fn serve(&self) -> Result<(), ()> { + /// Register a function which can be invoked by the front-end, object style + pub fn register_mut, F: Callable + 'static>( + &mut self, + name: S, + f: F, + ) -> &mut Self { + self.calls + .insert(name.into(), Arc::new(Mutex::new(Box::new(f)))); + self + } + + /// Run the web server instance forever, blocking this thread + #[cfg(feature = "blocking")] + pub fn run_blocking(&self) -> Result<(), ()> { let result = self.serve_internal(); tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -48,6 +60,11 @@ impl Instance { .block_on(result) } + /// Run the web server forever, asynchronously + pub async fn run(&self) -> Result<(), ()> { + self.serve_internal().await + } + fn handle_call( packet: socket::Packet, handlers: &HashMap, @@ -80,7 +97,7 @@ impl Instance { } /// Receive and execute callbacks forever - pub async fn serve_internal(&self) -> Result<(), ()> { + async fn serve_internal(&self) -> Result<(), ()> { let handlers = self.calls.clone(); //self.calls = HashMap::new(); let calls = warp::post() diff --git a/usdpl-back/src/lib.rs b/usdpl-back/src/lib.rs index 3a85a4e..440e633 100644 --- a/usdpl-back/src/lib.rs +++ b/usdpl-back/src/lib.rs @@ -1,8 +1,9 @@ //! Back-end library for plugins. //! Targets x86_64 (native Steam Deck ISA). //! -//! This is a minimalist TCP server for handling events from the front-end. +//! This is a minimalist web server for handling events from the front-end. //! +#![warn(missing_docs)] mod callable; //mod errors; @@ -12,6 +13,7 @@ pub use callable::Callable; pub use instance::Instance; //pub use errors::{ServerError, ServerResult}; +/// usdpl-core re-export pub mod core { pub use usdpl_core::*; } diff --git a/usdpl-core/Cargo.toml b/usdpl-core/Cargo.toml index e46f41d..5da1ee4 100644 --- a/usdpl-core/Cargo.toml +++ b/usdpl-core/Cargo.toml @@ -7,7 +7,10 @@ repository = "https://github.com/NGnius/usdpl-rs" readme = "README.md" description = "Universal Steam Deck Plugin Library core" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +decky = [] +crankshaft = [] [dependencies] base64 = "0.13" diff --git a/usdpl-core/README.md b/usdpl-core/README.md index e5e1f3a..d1b36d1 100644 --- a/usdpl-core/README.md +++ b/usdpl-core/README.md @@ -1,6 +1,6 @@ +[![Crates.io](https://img.shields.io/crates/v/usdpl-core?style=flat-square)](https://crates.io/crates/usdpl-core) + # usdpl-core Datatypes and constants core the back-end and front-end libraries' operation. This contains serialization functionality and networking datatypes. - -License: GPL-3.0-only diff --git a/usdpl-core/README.tpl b/usdpl-core/README.tpl new file mode 100644 index 0000000..d6dfe29 --- /dev/null +++ b/usdpl-core/README.tpl @@ -0,0 +1,5 @@ +[![Crates.io](https://img.shields.io/crates/v/usdpl-core?style=flat-square)](https://crates.io/crates/usdpl-core) + +# {{crate}} + +{{readme}} diff --git a/usdpl-core/src/api_common/target.rs b/usdpl-core/src/api_common/target.rs index 61ef1c6..ed7f3ca 100644 --- a/usdpl-core/src/api_common/target.rs +++ b/usdpl-core/src/api_common/target.rs @@ -1,10 +1,16 @@ +/// Supported plugin platforms pub enum Platform { + /// Generic platform Any, + /// Decky aka PluginLoader platform Decky, + /// Crankshaft platform Crankshaft, } impl Platform { + /// The current platform that usdpl-core is configured to target. + /// This is determined by feature flags. pub fn current() -> Self { #[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] { diff --git a/usdpl-core/src/lib.rs b/usdpl-core/src/lib.rs index a01d4a7..7090ffa 100644 --- a/usdpl-core/src/lib.rs +++ b/usdpl-core/src/lib.rs @@ -1,5 +1,7 @@ //! Datatypes and constants core the back-end and front-end libraries' operation. //! This contains serialization functionality and networking datatypes. +#![warn(missing_docs)] + mod remote_call; #[cfg(not(any(feature = "decky", feature = "crankshaft")))] @@ -15,6 +17,8 @@ pub mod socket; pub use remote_call::{RemoteCall, RemoteCallResponse}; +/// USDPL core API. +/// This contains functionality used in both the back-end and front-end. pub mod api { #[cfg(not(any(feature = "decky", feature = "crankshaft")))] pub use super::api_any::*; diff --git a/usdpl-core/src/remote_call.rs b/usdpl-core/src/remote_call.rs index 4b7f052..279495d 100644 --- a/usdpl-core/src/remote_call.rs +++ b/usdpl-core/src/remote_call.rs @@ -2,8 +2,11 @@ use crate::serdes::{DumpError, Dumpable, LoadError, Loadable, Primitive}; /// Remote call packet representing a function to call on the back-end, sent from the front-end pub struct RemoteCall { + /// The call id assigned by the front-end pub id: u64, + /// The function's name pub function: String, + /// The function's input parameters pub parameters: Vec, } @@ -34,7 +37,9 @@ impl Dumpable for RemoteCall { /// Remote call response packet representing the response from a remote call after the back-end has executed it. pub struct RemoteCallResponse { + /// The call id from the RemoteCall pub id: u64, + /// The function's result pub response: Vec, } diff --git a/usdpl-core/src/serdes/primitive.rs b/usdpl-core/src/serdes/primitive.rs index 585937b..0c43c62 100644 --- a/usdpl-core/src/serdes/primitive.rs +++ b/usdpl-core/src/serdes/primitive.rs @@ -3,19 +3,30 @@ use super::{DumpError, Dumpable, LoadError, Loadable}; /// Primitive types supported for communication between the USDPL back- and front-end. /// These are used for sending over the TCP connection. pub enum Primitive { + /// Null or unsupported object Empty, + /// String-like String(String), + /// f32 F32(f32), + /// f64 F64(f64), + /// u32 U32(u32), + /// u64 U64(u64), + /// i32 I32(i32), + /// i64 I64(i64), + /// boolean Bool(bool), + /// Non-primitive in Json format Json(String), } impl Primitive { + /// Discriminant -- first byte of a dumped primitive const fn discriminant(&self) -> u8 { match self { Self::Empty => 1, diff --git a/usdpl-core/src/serdes/traits.rs b/usdpl-core/src/serdes/traits.rs index 6a6f804..a0eb482 100644 --- a/usdpl-core/src/serdes/traits.rs +++ b/usdpl-core/src/serdes/traits.rs @@ -5,8 +5,11 @@ const B64_CONF: Config = Config::new(base64::CharacterSet::Standard, true); /// Errors from Loadable::load #[derive(Debug)] pub enum LoadError { + /// Buffer smaller than expected TooSmallBuffer, + /// Unexpected/corrupted data encountered InvalidData, + /// Unimplemented #[cfg(debug_assertions)] Todo, } @@ -28,6 +31,7 @@ pub trait Loadable: Sized { /// If anything is wrong with the buffer, None should be returned. fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError>; + /// Load data from a base64-encoded buffer fn load_base64(buffer: &[u8]) -> Result<(Self, usize), LoadError> { let mut buffer2 = [0u8; crate::socket::PACKET_BUFFER_SIZE]; let len = decode_config_slice(buffer, B64_CONF, &mut buffer2) @@ -39,8 +43,11 @@ pub trait Loadable: Sized { /// Errors from Dumpable::dump #[derive(Debug)] pub enum DumpError { + /// Buffer not big enough to dump data into TooSmallBuffer, + /// Data cannot be dumped Unsupported, + /// Unimplemented #[cfg(debug_assertions)] Todo, } @@ -62,6 +69,8 @@ pub trait Dumpable { /// If anything is wrong, false should be returned. fn dump(&self, buffer: &mut [u8]) -> Result; + /// Dump data as base64-encoded. + /// Useful for transmitting data as text. fn dump_base64(&self, buffer: &mut [u8]) -> Result { let mut buffer2 = [0u8; crate::socket::PACKET_BUFFER_SIZE]; let len = self.dump(&mut buffer2)?; diff --git a/usdpl-core/src/socket.rs b/usdpl-core/src/socket.rs index 20be3eb..bad4038 100644 --- a/usdpl-core/src/socket.rs +++ b/usdpl-core/src/socket.rs @@ -1,13 +1,18 @@ +//! Web messaging use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use crate::serdes::{DumpError, Dumpable, LoadError, Loadable}; use crate::{RemoteCall, RemoteCallResponse}; -pub const HOST_STR: &str = "127.0.0.1"; +/// Host IP address for web browsers +pub const HOST_STR: &str = "localhost"; +/// Host IP address pub const HOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); +/// Standard max packet size pub const PACKET_BUFFER_SIZE: usize = 1024; +/// Address and port #[inline] pub fn socket_addr(port: u16) -> SocketAddr { SocketAddr::V4(SocketAddrV4::new(HOST, port)) @@ -15,13 +20,21 @@ pub fn socket_addr(port: u16) -> SocketAddr { /// Accepted Packet types and the data they contain pub enum Packet { + /// A remote call Call(RemoteCall), + /// A reponse to a remote call CallResponse(RemoteCallResponse), + /// Unused KeepAlive, + /// Invalid Invalid, + /// General message Message(String), + /// Response to an unsupported packet Unsupported, + /// Broken packet type, useful for testing Bad, + /// Many packets merged into one Many(Vec), } diff --git a/usdpl-front/Cargo.toml b/usdpl-front/Cargo.toml index ea385d4..4640ff4 100644 --- a/usdpl-front/Cargo.toml +++ b/usdpl-front/Cargo.toml @@ -1,20 +1,21 @@ [package] -name = "usdpl" +name = "usdpl-front" version = "0.4.0" authors = ["NGnius (Graham) "] edition = "2021" license = "GPL-3.0-only" repository = "https://github.com/NGnius/usdpl-rs" -readme = "../README.md" +readme = "README.md" description = "Universal Steam Deck Plugin Library front-end designed for WASM" [lib] crate-type = ["cdylib", "rlib"] [features] -default = ["console_error_panic_hook"] -decky = [] -crankshaft = [] +default = [] +decky = ["usdpl-core/decky"] +crankshaft = ["usdpl-core/crankshaft"] +debug = ["console_error_panic_hook"] [dependencies] wasm-bindgen = "0.2" @@ -40,14 +41,10 @@ web-sys = { version = "0.3", features = [ 'RequestMode', 'Response', 'Window', -]}#["WebSocket", "MessageEvent", "ErrorEvent", "BinaryType"] } +]} js-sys = { version = "0.3" } usdpl-core = { version = "0.4.0", path = "../usdpl-core" } [dev-dependencies] -wasm-bindgen-test = "0.3.13" - -#[profile.release] -# Tell `rustc` to optimize for small code size. -#opt-level = "s" +wasm-bindgen-test = { version = "0.3.13" } diff --git a/usdpl-front/README.md b/usdpl-front/README.md index e69de29..fd771f8 100644 --- a/usdpl-front/README.md +++ b/usdpl-front/README.md @@ -0,0 +1,9 @@ +[![Crates.io](https://img.shields.io/crates/v/usdpl-front?style=flat-square)](https://crates.io/crates/usdpl-front) + +# usdpl-front-front + +Front-end library to be called from Javascript. +Targets WASM. + +In true Javascript tradition, this part of the library does not support error handling. + diff --git a/usdpl-front/README.tpl b/usdpl-front/README.tpl new file mode 100644 index 0000000..221287d --- /dev/null +++ b/usdpl-front/README.tpl @@ -0,0 +1,5 @@ +[![Crates.io](https://img.shields.io/crates/v/usdpl-front?style=flat-square)](https://crates.io/crates/usdpl-front) + +# {{crate}}-front + +{{readme}} diff --git a/usdpl-front/scripts/generate_embedded_wasm.py b/usdpl-front/scripts/generate_embedded_wasm.py index a6699df..d6d6638 100644 --- a/usdpl-front/scripts/generate_embedded_wasm.py +++ b/usdpl-front/scripts/generate_embedded_wasm.py @@ -1,11 +1,11 @@ import base64 if __name__ == "__main__": - print("Embedding WASM into udspl.js") + print("Embedding WASM into udspl_front.js") # assumption: current working directory (relative to this script) is ../ # assumption: release wasm binary at ./pkg/usdpl_bg.wasm - with open("./pkg/usdpl_bg.wasm", mode="rb") as infile: - with open("./pkg/usdpl.js", mode="ab") as outfile: + with open("./pkg/usdpl_front_bg.wasm", mode="rb") as infile: + with open("./pkg/usdpl_front.js", mode="ab") as outfile: outfile.write("\n\n// USDPL customization\nconst encoded = \"".encode()) encoded = base64.b64encode(infile.read()) outfile.write(encoded) @@ -32,6 +32,6 @@ export function init_embedded() { return init(decode()) } """.encode()) - with open("./pkg/usdpl.d.ts", "a") as outfile: + with open("./pkg/usdpl_front.d.ts", "a") as outfile: outfile.write("\n\n// USDPL customization\nexport function init_embedded();\n") - print("Done: Embedded WASM into udspl.js") + print("Done: Embedded WASM into udspl_front.js") diff --git a/usdpl-front/src/imports.rs b/usdpl-front/src/imports.rs index 92f9f9a..8e13b48 100644 --- a/usdpl-front/src/imports.rs +++ b/usdpl-front/src/imports.rs @@ -2,12 +2,15 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { + #[cfg(feature = "debug")] #[wasm_bindgen(js_namespace = console, js_name = log)] pub fn console_log(s: &str); + #[cfg(feature = "debug")] #[wasm_bindgen(js_namespace = console, js_name = warn)] pub fn console_warn(s: &str); + #[cfg(feature = "debug")] #[wasm_bindgen(js_namespace = console, js_name = error)] pub fn console_error(s: &str); } diff --git a/usdpl-front/src/lib.rs b/usdpl-front/src/lib.rs index d47a11c..a91f018 100644 --- a/usdpl-front/src/lib.rs +++ b/usdpl-front/src/lib.rs @@ -3,6 +3,7 @@ //! //! In true Javascript tradition, this part of the library does not support error handling. //! +#![warn(missing_docs)] mod connection; mod convert; @@ -17,9 +18,9 @@ use usdpl_core::{socket::Packet, RemoteCall}; static mut CTX: UsdplContext = UsdplContext { port: 31337, id: 1 }; -#[wasm_bindgen] +//#[wasm_bindgen] #[derive(Debug)] -pub struct UsdplContext { +struct UsdplContext { port: u16, id: u64, } @@ -62,6 +63,7 @@ pub fn target() -> String { /// Returns null (None) if this fails for any reason. #[wasm_bindgen] pub async fn call_backend(name: String, parameters: Vec) -> JsValue { + #[cfg(feature = "debug")] imports::console_log(&format!( "call_backend({}, [params; {}])", name, @@ -73,6 +75,7 @@ pub async fn call_backend(name: String, parameters: Vec) -> JsValue { params.push(convert::js_to_primitive(val)); } let port = get_port(); + #[cfg(feature = "debug")] imports::console_log(&format!("USDPL: Got port {}", port)); let results = connection::send_js( Packet::Call(RemoteCall { @@ -85,7 +88,9 @@ pub async fn call_backend(name: String, parameters: Vec) -> JsValue { .await; let results = match results { Ok(x) => x, + #[allow(unused_variables)] Err(e) => { + #[cfg(feature = "debug")] imports::console_error(&format!("USDPL: Got error while calling {}: {:?}", name, e)); return JsValue::NULL; }