Compare commits
17 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
44b035517a | ||
![]() |
8d16b8159e | ||
![]() |
c5df5eb6d3 | ||
![]() |
f873ed6587 | ||
![]() |
24e0e30d3e | ||
![]() |
df353bd87d | ||
![]() |
03e7491e12 | ||
![]() |
6c6c9a132a | ||
![]() |
31613dbac9 | ||
![]() |
7435f6c136 | ||
![]() |
5559d24a9f | ||
![]() |
aba76a4b00 | ||
![]() |
6b52067a95 | ||
![]() |
d97f98485b | ||
![]() |
8f351bd639 | ||
![]() |
139eb3d31a | ||
![]() |
10b36c0621 |
1073
Cargo.lock
generated
1073
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -3,7 +3,7 @@ members = [
|
|||
"frontend",
|
||||
"backend",
|
||||
"shared",
|
||||
"src-tauri",
|
||||
"src-tauri", "test_files/components/rust_decoder",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
|
|
22
README.md
22
README.md
|
@ -3,34 +3,44 @@
|
|||
|
||||
---
|
||||
|
||||
<p align="center">Browser (Firefox)</p>
|
||||
<p align="center">
|
||||
<img width="800" src="docs/screenshot_firefox.png" alt="Fastwave - Browser (Firefox)" />
|
||||
Browser (Firefox)
|
||||
</p>
|
||||
|
||||
<p align="center">Desktop, miller columns and tree</p>
|
||||
<p align="center">
|
||||
<img width="800" src="docs/video_desktop.gif" alt="Fastwave - Desktop, miller columns and tree" />
|
||||
Desktop, miller columns and tree
|
||||
</p>
|
||||
|
||||
<p align="center">Zoom, pan and basic number formats</p>
|
||||
<p align="center">
|
||||
<img width="800" src="docs/video_zoom_formatting_simple.gif" alt="Fastwave - Zoom, pan and basic number formats" />
|
||||
Zoom, pan and basic number formats
|
||||
</p>
|
||||
|
||||
<p align="center">Zoom and all formats</p>
|
||||
<p align="center">
|
||||
<img width="800" src="docs/video_zoom_formatting.gif" alt="Fastwave - Zoom and all formats" />
|
||||
Zoom and all formats
|
||||
</p>
|
||||
|
||||
<p align="center">Javascript commands</p>
|
||||
<p align="center">
|
||||
<img width="800" src="docs/video_javascript_commands.gif" alt="Fastwave - Javascript commands" />
|
||||
Javascript commands
|
||||
</p>
|
||||
|
||||
<p align="center">Load and save selected variables</p>
|
||||
<p align="center">
|
||||
<img width="800" src="docs/video_load_save_selected_vars.gif" alt="Fastwave - Load and save selected variables" />
|
||||
Load and save selected variables
|
||||
</p>
|
||||
|
||||
<p align="center">Decoders Demo</p>
|
||||
<p align="center">
|
||||
<img width="800" src="docs/video_decoders.gif" alt="Fastwave - Decoders demo" />
|
||||
</p>
|
||||
|
||||
<p align="center">Decoder Interface</p>
|
||||
<p align="center">
|
||||
<img width="500" src="docs/screenshot_world_wit.png" alt="Fastwave - Decoder Interface" />
|
||||
</p>
|
||||
|
||||
---
|
||||
|
|
BIN
docs/screenshot_world_wit.png
Normal file
BIN
docs/screenshot_world_wit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
BIN
docs/video_decoders.gif
Normal file
BIN
docs/video_decoders.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 764 KiB |
|
@ -241,8 +241,10 @@ impl HeaderPanel {
|
|||
if raw_event.shift_key() {
|
||||
// @TODO move `prevent_default` to MZ API (next to the `pass_to_parent` method?)
|
||||
raw_event.prevent_default();
|
||||
let result = script_bridge::strict_eval(&script.lock_ref());
|
||||
Task::start(clone!((script, command_result) async move {
|
||||
let result = script_bridge::strict_eval(&script.lock_ref()).await;
|
||||
command_result.set(Some(result));
|
||||
}));
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,7 +8,7 @@ mod controls_panel;
|
|||
use controls_panel::ControlsPanel;
|
||||
|
||||
mod waveform_panel;
|
||||
use waveform_panel::WaveformPanel;
|
||||
use waveform_panel::{PixiController, WaveformPanel};
|
||||
|
||||
mod header_panel;
|
||||
use header_panel::HeaderPanel;
|
||||
|
@ -27,6 +27,7 @@ struct Store {
|
|||
selected_var_refs: MutableVec<wellen::VarRef>,
|
||||
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
|
||||
loaded_filename: Mutable<Option<Filename>>,
|
||||
canvas_controller: Mutable<Mutable<Option<SendWrapper<PixiController>>>>,
|
||||
}
|
||||
|
||||
static STORE: Lazy<Store> = lazy::default();
|
||||
|
@ -45,6 +46,7 @@ fn root() -> impl Element {
|
|||
let selected_var_refs = STORE.selected_var_refs.clone();
|
||||
let layout: Mutable<Layout> = <_>::default();
|
||||
let loaded_filename = STORE.loaded_filename.clone();
|
||||
let canvas_controller = STORE.canvas_controller.clone();
|
||||
Column::new()
|
||||
.s(Height::fill())
|
||||
.s(Scrollbars::y_and_clip_x())
|
||||
|
@ -69,13 +71,15 @@ fn root() -> impl Element {
|
|||
let hierarchy = hierarchy.clone();
|
||||
let selected_var_refs = selected_var_refs.clone();
|
||||
let loaded_filename = loaded_filename.clone();
|
||||
let canvas_controller = canvas_controller.clone();
|
||||
map_ref!{
|
||||
let layout = layout.signal(),
|
||||
let hierarchy_is_some = hierarchy.signal_ref(Option::is_some) => {
|
||||
(*hierarchy_is_some && matches!(layout, Layout::Tree)).then(clone!((hierarchy, selected_var_refs, loaded_filename) move || WaveformPanel::new(
|
||||
(*hierarchy_is_some && matches!(layout, Layout::Tree)).then(clone!((hierarchy, selected_var_refs, loaded_filename, canvas_controller) move || WaveformPanel::new(
|
||||
hierarchy.clone(),
|
||||
selected_var_refs.clone(),
|
||||
loaded_filename.clone(),
|
||||
canvas_controller.clone(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
@ -85,10 +89,11 @@ fn root() -> impl Element {
|
|||
map_ref!{
|
||||
let layout = layout.signal(),
|
||||
let hierarchy_is_some = hierarchy.signal_ref(Option::is_some) => {
|
||||
(*hierarchy_is_some && matches!(layout, Layout::Columns)).then(clone!((hierarchy, selected_var_refs, loaded_filename) move || WaveformPanel::new(
|
||||
(*hierarchy_is_some && matches!(layout, Layout::Columns)).then(clone!((hierarchy, selected_var_refs, loaded_filename, canvas_controller) move || WaveformPanel::new(
|
||||
hierarchy.clone(),
|
||||
selected_var_refs.clone(),
|
||||
loaded_filename.clone(),
|
||||
canvas_controller.clone(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
// NOTE: `FASTWAVE_PLATFORM` is set in `Makefile.toml` tasks and then in `build.rs`
|
||||
|
||||
use crate::STORE;
|
||||
|
||||
#[cfg(FASTWAVE_PLATFORM = "TAURI")]
|
||||
mod tauri;
|
||||
#[cfg(FASTWAVE_PLATFORM = "TAURI")]
|
||||
|
@ -15,6 +17,9 @@ use browser as platform;
|
|||
|
||||
type Filename = String;
|
||||
type JavascriptCode = String;
|
||||
type AddedDecodersCount = usize;
|
||||
type RemovedDecodersCount = usize;
|
||||
type DecoderPath = String;
|
||||
|
||||
pub async fn show_window() {
|
||||
platform::show_window().await
|
||||
|
@ -58,3 +63,25 @@ pub async fn load_signal_and_get_timeline(
|
|||
pub async fn unload_signal(signal_ref: wellen::SignalRef) {
|
||||
platform::unload_signal(signal_ref).await
|
||||
}
|
||||
|
||||
pub async fn add_decoders(decoder_paths: Vec<DecoderPath>) -> AddedDecodersCount {
|
||||
let count = platform::add_decoders(decoder_paths).await;
|
||||
if count > 0 {
|
||||
redraw_all_timeline_rows().await;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
pub async fn remove_all_decoders() -> RemovedDecodersCount {
|
||||
let count = platform::remove_all_decoders().await;
|
||||
if count > 0 {
|
||||
redraw_all_timeline_rows().await;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
async fn redraw_all_timeline_rows() {
|
||||
if let Some(controller) = STORE.canvas_controller.get_cloned().get_cloned() {
|
||||
controller.redraw_all_rows().await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use shared::wellen_helpers;
|
||||
use std::sync::Mutex;
|
||||
use wellen::simple::Waveform;
|
||||
use zoon::*;
|
||||
use zoon::{eprintln, *};
|
||||
|
||||
#[derive(Default)]
|
||||
struct BrowserPlatformStore {
|
||||
|
@ -111,7 +111,9 @@ pub(super) async fn load_signal_and_get_timeline(
|
|||
timeline_viewport_x,
|
||||
block_height,
|
||||
var_format,
|
||||
);
|
||||
|value| Box::pin(async { value }),
|
||||
)
|
||||
.await;
|
||||
timeline
|
||||
}
|
||||
|
||||
|
@ -120,3 +122,17 @@ pub(super) async fn unload_signal(signal_ref: wellen::SignalRef) {
|
|||
let waveform = waveform_lock.as_mut().unwrap_throw();
|
||||
waveform.unload_signals(&[signal_ref]);
|
||||
}
|
||||
|
||||
pub(super) async fn add_decoders(
|
||||
_decoder_paths: Vec<super::DecoderPath>,
|
||||
) -> super::AddedDecodersCount {
|
||||
// @TODO error message for user
|
||||
eprintln!("Adding decoders is not supported in the browser.");
|
||||
0
|
||||
}
|
||||
|
||||
pub(super) async fn remove_all_decoders() -> super::RemovedDecodersCount {
|
||||
// @TODO error message for user
|
||||
eprintln!("Removing decoders is not supported in the browser.");
|
||||
0
|
||||
}
|
||||
|
|
|
@ -56,6 +56,18 @@ pub(super) async fn unload_signal(signal_ref: wellen::SignalRef) {
|
|||
.unwrap_throw()
|
||||
}
|
||||
|
||||
pub(super) async fn add_decoders(
|
||||
decoder_paths: Vec<super::DecoderPath>,
|
||||
) -> super::AddedDecodersCount {
|
||||
serde_wasm_bindgen::from_value(tauri_glue::add_decoders(decoder_paths).await.unwrap_throw())
|
||||
.unwrap_throw()
|
||||
}
|
||||
|
||||
pub(super) async fn remove_all_decoders() -> super::RemovedDecodersCount {
|
||||
serde_wasm_bindgen::from_value(tauri_glue::remove_all_decoders().await.unwrap_throw())
|
||||
.unwrap_throw()
|
||||
}
|
||||
|
||||
mod tauri_glue {
|
||||
use zoon::*;
|
||||
|
||||
|
@ -86,5 +98,13 @@ mod tauri_glue {
|
|||
|
||||
#[wasm_bindgen(catch)]
|
||||
pub async fn unload_signal(signal_ref_index: usize) -> Result<(), JsValue>;
|
||||
|
||||
#[wasm_bindgen(catch)]
|
||||
pub async fn add_decoders(
|
||||
decoder_paths: Vec<super::super::DecoderPath>,
|
||||
) -> Result<JsValue, JsValue>;
|
||||
|
||||
#[wasm_bindgen(catch)]
|
||||
pub async fn remove_all_decoders() -> Result<JsValue, JsValue>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use crate::STORE;
|
||||
use crate::{platform, STORE};
|
||||
use wellen::GetItem;
|
||||
use zoon::*;
|
||||
|
||||
type FullVarName = String;
|
||||
type AddedDecodersCount = usize;
|
||||
type RemovedDecodersCount = usize;
|
||||
type DecoderPath = String;
|
||||
|
||||
#[wasm_bindgen(
|
||||
inline_js = r#"export function strict_eval(code) { "use strict"; return eval?.(`${code}`) }"#
|
||||
)]
|
||||
#[wasm_bindgen(module = "/typescript/bundles/strict_eval.js")]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(catch)]
|
||||
pub fn strict_eval(code: &str) -> Result<JsValue, JsValue>;
|
||||
pub async fn strict_eval(code: &str) -> Result<JsValue, JsValue>;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
@ -67,4 +68,14 @@ impl FW {
|
|||
}
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// JS: `FW.add_decoders(["../test_files/components/rust_decoder/rust_decoder.wasm"])` -> `1`
|
||||
pub async fn add_decoders(decoder_paths: Vec<DecoderPath>) -> AddedDecodersCount {
|
||||
platform::add_decoders(decoder_paths).await
|
||||
}
|
||||
|
||||
/// JS: `FW.remove_all_decoders()` -> `5`
|
||||
pub async fn remove_all_decoders() -> RemovedDecodersCount {
|
||||
platform::remove_all_decoders().await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ use wellen::GetItem;
|
|||
use zoon::*;
|
||||
|
||||
mod pixi_canvas;
|
||||
use pixi_canvas::{PixiCanvas, PixiController};
|
||||
use pixi_canvas::PixiCanvas;
|
||||
pub use pixi_canvas::PixiController;
|
||||
|
||||
const ROW_HEIGHT: u32 = 40;
|
||||
const ROW_GAP: u32 = 4;
|
||||
|
@ -14,7 +15,7 @@ pub struct WaveformPanel {
|
|||
selected_var_refs: MutableVec<wellen::VarRef>,
|
||||
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
|
||||
loaded_filename: Mutable<Option<Filename>>,
|
||||
canvas_controller: Mutable<ReadOnlyMutable<Option<PixiController>>>,
|
||||
canvas_controller: Mutable<Mutable<Option<SendWrapper<PixiController>>>>,
|
||||
}
|
||||
|
||||
impl WaveformPanel {
|
||||
|
@ -22,12 +23,13 @@ impl WaveformPanel {
|
|||
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
|
||||
selected_var_refs: MutableVec<wellen::VarRef>,
|
||||
loaded_filename: Mutable<Option<Filename>>,
|
||||
canvas_controller: Mutable<Mutable<Option<SendWrapper<PixiController>>>>,
|
||||
) -> impl Element {
|
||||
Self {
|
||||
selected_var_refs,
|
||||
hierarchy,
|
||||
loaded_filename,
|
||||
canvas_controller: Mutable::new(Mutable::default().read_only()),
|
||||
canvas_controller,
|
||||
}
|
||||
.root()
|
||||
}
|
||||
|
@ -96,7 +98,7 @@ impl WaveformPanel {
|
|||
if let Some(javascript_code) =
|
||||
platform::load_file_with_selected_vars(None).await
|
||||
{
|
||||
match script_bridge::strict_eval(&javascript_code) {
|
||||
match script_bridge::strict_eval(&javascript_code).await {
|
||||
Ok(js_value) => {
|
||||
zoon::println!("File with selected vars loaded: {js_value:?}")
|
||||
}
|
||||
|
@ -153,7 +155,7 @@ impl WaveformPanel {
|
|||
if let Some(javascript_code) =
|
||||
platform::load_file_with_selected_vars(Some(file)).await
|
||||
{
|
||||
match script_bridge::strict_eval(&javascript_code) {
|
||||
match script_bridge::strict_eval(&javascript_code).await {
|
||||
Ok(js_value) => zoon::println!(
|
||||
"File with selected vars loaded: {js_value:?}"
|
||||
),
|
||||
|
|
|
@ -5,7 +5,7 @@ use zoon::*;
|
|||
|
||||
pub struct PixiCanvas {
|
||||
raw_el: RawHtmlEl<web_sys::HtmlElement>,
|
||||
controller: ReadOnlyMutable<Option<js_bridge::PixiController>>,
|
||||
controller: Mutable<Option<SendWrapper<js_bridge::PixiController>>>,
|
||||
#[allow(dead_code)]
|
||||
width: ReadOnlyMutable<u32>,
|
||||
#[allow(dead_code)]
|
||||
|
@ -32,7 +32,8 @@ impl HasIds for PixiCanvas {}
|
|||
|
||||
impl PixiCanvas {
|
||||
pub fn new(row_height: u32, row_gap: u32) -> Self {
|
||||
let controller: Mutable<Option<js_bridge::PixiController>> = Mutable::new(None);
|
||||
let controller: Mutable<Option<SendWrapper<js_bridge::PixiController>>> =
|
||||
Mutable::new(None);
|
||||
let width = Mutable::new(0);
|
||||
let height = Mutable::new(0);
|
||||
let resize_task = Task::start_droppable(
|
||||
|
@ -77,7 +78,7 @@ impl PixiCanvas {
|
|||
));
|
||||
// -- // --
|
||||
Self {
|
||||
controller: controller.read_only(),
|
||||
controller: controller.clone(),
|
||||
width: width.read_only(),
|
||||
height: height.read_only(),
|
||||
task_with_controller: task_with_controller.clone(),
|
||||
|
@ -105,14 +106,14 @@ impl PixiCanvas {
|
|||
})
|
||||
.after_insert(clone!((controller, timeline_getter) move |element| {
|
||||
Task::start(async move {
|
||||
let pixi_controller = js_bridge::PixiController::new(
|
||||
let pixi_controller = SendWrapper::new(js_bridge::PixiController::new(
|
||||
1.,
|
||||
width.get(),
|
||||
0,
|
||||
row_height,
|
||||
row_gap,
|
||||
&timeline_getter
|
||||
);
|
||||
));
|
||||
pixi_controller.init(&element).await;
|
||||
controller.set(Some(pixi_controller));
|
||||
});
|
||||
|
@ -131,7 +132,7 @@ impl PixiCanvas {
|
|||
|
||||
pub fn task_with_controller<FUT: Future<Output = ()> + 'static>(
|
||||
self,
|
||||
f: impl FnOnce(ReadOnlyMutable<Option<js_bridge::PixiController>>) -> FUT,
|
||||
f: impl FnOnce(Mutable<Option<SendWrapper<js_bridge::PixiController>>>) -> FUT,
|
||||
) -> Self {
|
||||
self.task_with_controller
|
||||
.set(Some(Task::start_droppable(f(self.controller.clone()))));
|
||||
|
@ -186,6 +187,8 @@ mod js_bridge {
|
|||
#[wasm_bindgen(method)]
|
||||
pub fn destroy(this: &PixiController);
|
||||
|
||||
// -- FastWave-specific --
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn get_timeline_zoom(this: &PixiController) -> f64;
|
||||
|
||||
|
@ -195,8 +198,6 @@ mod js_bridge {
|
|||
#[wasm_bindgen(method)]
|
||||
pub fn get_timeline_viewport_x(this: &PixiController) -> i32;
|
||||
|
||||
// -- FastWave-specific --
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn set_var_format(this: &PixiController, index: usize, var_format: JsValue);
|
||||
|
||||
|
@ -224,5 +225,8 @@ mod js_bridge {
|
|||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn clear_vars(this: &PixiController);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub async fn redraw_all_rows(this: &PixiController);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35265,6 +35265,8 @@ var PixiController = class {
|
|||
clear_vars() {
|
||||
this.var_signal_rows.slice().reverse().forEach((row) => row.destroy());
|
||||
}
|
||||
request_timeline_redraw() {
|
||||
}
|
||||
};
|
||||
var VarSignalRow = class {
|
||||
signal_ref_index;
|
||||
|
|
5
frontend/typescript/bundles/strict_eval.js
Normal file
5
frontend/typescript/bundles/strict_eval.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
export async function strict_eval(code) {
|
||||
return await eval(code)
|
||||
}
|
|
@ -2536,11 +2536,19 @@ async function load_signal_and_get_timeline(signal_ref_index, timeline_zoom, tim
|
|||
async function unload_signal(signal_ref_index) {
|
||||
return await invoke2("unload_signal", { signal_ref_index });
|
||||
}
|
||||
async function add_decoders(decoder_paths) {
|
||||
return await invoke2("add_decoders", { decoder_paths });
|
||||
}
|
||||
async function remove_all_decoders() {
|
||||
return await invoke2("remove_all_decoders");
|
||||
}
|
||||
export {
|
||||
add_decoders,
|
||||
get_hierarchy,
|
||||
load_file_with_selected_vars,
|
||||
load_signal_and_get_timeline,
|
||||
pick_and_load_waveform,
|
||||
remove_all_decoders,
|
||||
show_window,
|
||||
unload_signal
|
||||
};
|
||||
|
|
|
@ -199,6 +199,10 @@ export class PixiController {
|
|||
clear_vars() {
|
||||
this.var_signal_rows.slice().reverse().forEach(row => row.destroy());
|
||||
}
|
||||
|
||||
request_timeline_redraw() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class VarSignalRow {
|
||||
|
|
|
@ -9,6 +9,9 @@ type JavascriptCode = string;
|
|||
type WellenHierarchy = unknown;
|
||||
type Timeline = unknown;
|
||||
type VarFormat = unknown;
|
||||
type AddedDecodersCount = number;
|
||||
type RemovedDecodersCount = number;
|
||||
type DecoderPath = string;
|
||||
|
||||
export async function show_window(): Promise<void> {
|
||||
return await invoke("show_window");
|
||||
|
@ -47,3 +50,11 @@ export async function load_signal_and_get_timeline(
|
|||
export async function unload_signal(signal_ref_index: number): Promise<void> {
|
||||
return await invoke("unload_signal", { signal_ref_index });
|
||||
}
|
||||
|
||||
export async function add_decoders(decoder_paths: Array<DecoderPath>): Promise<AddedDecodersCount> {
|
||||
return await invoke("add_decoders", { decoder_paths });
|
||||
}
|
||||
|
||||
export async function remove_all_decoders(): Promise<RemovedDecodersCount> {
|
||||
return await invoke("remove_all_decoders");
|
||||
}
|
||||
|
|
|
@ -1,13 +1,25 @@
|
|||
use crate::*;
|
||||
use future::BoxFuture;
|
||||
use wellen::SignalValue;
|
||||
|
||||
pub fn signal_to_timeline(
|
||||
signal: &wellen::Signal,
|
||||
// @TODO remove once https://github.com/rust-lang/rust/issues/89976 is resolved
|
||||
// (`error: implementation of `FnOnce` is not general enough`)
|
||||
fn type_hint<F>(f: F) -> F
|
||||
where
|
||||
F: for<'a> FnMut((u32, SignalValue<'a>)) -> (f64, SignalValue<'a>),
|
||||
{
|
||||
f
|
||||
}
|
||||
|
||||
pub async fn signal_to_timeline<'s>(
|
||||
signal: &'s wellen::Signal,
|
||||
time_table: &[wellen::Time],
|
||||
timeline_zoom: f64,
|
||||
timeline_viewport_width: u32,
|
||||
timeline_viewport_x: i32,
|
||||
block_height: u32,
|
||||
var_format: VarFormat,
|
||||
mut format_by_decoders: impl FnMut(String) -> BoxFuture<'s, String>,
|
||||
) -> Timeline {
|
||||
const MIN_BLOCK_WIDTH: u32 = 3;
|
||||
// Courier New, 16px, sync with `label_style` in `pixi_canvas.rs`
|
||||
|
@ -25,12 +37,12 @@ pub fn signal_to_timeline(
|
|||
|
||||
let mut x_value_pairs = signal
|
||||
.iter_changes()
|
||||
.map(|(index, value)| {
|
||||
.map(type_hint(move |(index, value)| {
|
||||
let index = index as usize;
|
||||
let time = time_table[index] as f64;
|
||||
let x = time / last_time * timeline_width - timeline_viewport_x;
|
||||
(x, value)
|
||||
})
|
||||
}))
|
||||
.peekable();
|
||||
|
||||
// @TODO parallelize?
|
||||
|
@ -55,7 +67,8 @@ pub fn signal_to_timeline(
|
|||
}
|
||||
|
||||
// @TODO cache?
|
||||
let value = var_format.format(value);
|
||||
let mut value = var_format.format(value);
|
||||
value = format_by_decoders(value).await;
|
||||
|
||||
let value_width = (value.chars().count() as f64 * LETTER_WIDTH) as u32;
|
||||
// @TODO Ellipsis instead of hiding?
|
||||
|
|
|
@ -23,3 +23,11 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
tauri = { version = "=2.0.0-beta.22", features = ["macos-private-api", "linux-ipc-protocol"] }
|
||||
tauri-plugin-window-state = "=2.0.0-beta.9"
|
||||
tauri-plugin-dialog = "=2.0.0-beta.9"
|
||||
once_cell = "1.19.0"
|
||||
futures = "0.3.30"
|
||||
|
||||
# wasmtime = "22.0.0"
|
||||
# wasmtime-wasi = "22.0.0"
|
||||
# ~23.0.0
|
||||
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "842fa767acdc26f096ac108605353b8b71e23169" }
|
||||
wasmtime-wasi = { git = "https://github.com/bytecodealliance/wasmtime", rev = "842fa767acdc26f096ac108605353b8b71e23169" }
|
||||
|
|
125
src-tauri/src/component_manager.rs
Normal file
125
src-tauri/src/component_manager.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
use crate::{AddedDecodersCount, DecoderPath, RemovedDecodersCount};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
use tauri::async_runtime::{Mutex, RwLock};
|
||||
use wasmtime::component::{Component as WasmtimeComponent, *};
|
||||
use wasmtime::{AsContextMut, Engine, Store};
|
||||
use wasmtime_wasi::{WasiCtx, WasiView};
|
||||
|
||||
bindgen!();
|
||||
|
||||
pub static DECODERS: Lazy<Arc<RwLock<Vec<Component>>>> = Lazy::new(<_>::default);
|
||||
static ENGINE: Lazy<Engine> = Lazy::new(<_>::default);
|
||||
static LINKER: Lazy<Linker<State>> = Lazy::new(|| {
|
||||
let mut linker = Linker::new(&ENGINE);
|
||||
wasmtime_wasi::add_to_linker_sync(&mut linker).unwrap();
|
||||
Component::add_to_linker(&mut linker, |state: &mut State| state).unwrap();
|
||||
linker
|
||||
});
|
||||
pub static STORE: Lazy<Arc<Mutex<Store<State>>>> = Lazy::new(|| {
|
||||
let store = Store::new(
|
||||
&ENGINE,
|
||||
State {
|
||||
ctx: WasiCtx::builder().build(),
|
||||
table: ResourceTable::new(),
|
||||
},
|
||||
);
|
||||
Arc::new(Mutex::new(store))
|
||||
});
|
||||
|
||||
pub struct State {
|
||||
ctx: WasiCtx,
|
||||
table: ResourceTable,
|
||||
}
|
||||
|
||||
impl WasiView for State {
|
||||
fn ctx(&mut self) -> &mut WasiCtx {
|
||||
&mut self.ctx
|
||||
}
|
||||
fn table(&mut self) -> &mut ResourceTable {
|
||||
&mut self.table
|
||||
}
|
||||
}
|
||||
|
||||
impl component::decoder::host::Host for State {
|
||||
fn log(&mut self, message: String) {
|
||||
println!("Decoder: {message}");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_all_decoders() -> RemovedDecodersCount {
|
||||
let mut decoders = DECODERS.write().await;
|
||||
let decoders_count = decoders.len();
|
||||
decoders.clear();
|
||||
decoders_count
|
||||
}
|
||||
|
||||
// @TODO Make println work on Windows in release mode?
|
||||
// https://github.com/tauri-apps/tauri/discussions/8626
|
||||
|
||||
// @TODO Remove / improve comments below
|
||||
// Testing
|
||||
//
|
||||
// Rust
|
||||
// FW.add_decoders(["../test_files/components/rust_decoder/rust_decoder.wasm"])
|
||||
// FW.add_decoders(["../test_files/components/rust_decoder/rust_decoder.wasm", "../test_files/components/rust_decoder/rust_decoder.wasm"])
|
||||
//
|
||||
// JS
|
||||
// FW.add_decoders(["../test_files/components/javascript_decoder/javascript_decoder.wasm"])
|
||||
//
|
||||
// Python
|
||||
// FW.add_decoders(["../test_files/components/python_decoder/python_decoder.wasm"])
|
||||
//
|
||||
// Remove all
|
||||
// FW.remove_all_decoders()
|
||||
//
|
||||
// All Debug
|
||||
// FW.add_decoders(["../test_files/components/rust_decoder/rust_decoder.wasm", "../test_files/components/javascript_decoder/javascript_decoder.wasm", "../test_files/components/python_decoder/python_decoder.wasm"])
|
||||
//
|
||||
// All Release
|
||||
// FW.add_decoders(["../../test_files/components/rust_decoder/rust_decoder.wasm", "../../test_files/components/javascript_decoder/javascript_decoder.wasm", "../../test_files/components/python_decoder/python_decoder.wasm"])
|
||||
pub async fn add_decoders(decoder_paths: Vec<DecoderPath>) -> AddedDecodersCount {
|
||||
println!("Decoders: {decoder_paths:#?}");
|
||||
println!("Current dir: {:#?}", std::env::current_dir().unwrap());
|
||||
|
||||
let mut added_decoders_count = 0;
|
||||
|
||||
// @TODO (?) New thread to prevent "Cannot start a runtime from within a runtime."
|
||||
// when a call to a component fails / panics
|
||||
// std::thread::spawn(move || {
|
||||
// futures::executor::block_on(async move {
|
||||
for decoder_path in decoder_paths {
|
||||
if let Err(error) = add_decoder(&decoder_path).await {
|
||||
eprintln!("add_decoders error: {error:?}");
|
||||
} else {
|
||||
added_decoders_count += 1;
|
||||
}
|
||||
}
|
||||
// })
|
||||
// }).join().unwrap();
|
||||
|
||||
added_decoders_count
|
||||
}
|
||||
|
||||
async fn add_decoder(path: &str) -> wasmtime::Result<()> {
|
||||
let wasmtime_component = WasmtimeComponent::from_file(&ENGINE, path)?;
|
||||
|
||||
let mut store_lock = STORE.lock().await;
|
||||
let mut store = store_lock.as_context_mut();
|
||||
|
||||
let component = Component::instantiate(&mut store, &wasmtime_component, &LINKER)?;
|
||||
|
||||
println!(
|
||||
"Decoder name: {}",
|
||||
component
|
||||
.component_decoder_decoder()
|
||||
.call_name(&mut store)?
|
||||
);
|
||||
component
|
||||
.component_decoder_decoder()
|
||||
.call_init(&mut store)?;
|
||||
|
||||
DECODERS.write().await.push(component);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,14 +1,20 @@
|
|||
use std::fs;
|
||||
use std::sync::Mutex;
|
||||
use tauri::async_runtime::RwLock;
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
use wasmtime::AsContextMut;
|
||||
use wellen::simple::Waveform;
|
||||
|
||||
type Filename = String;
|
||||
type JavascriptCode = String;
|
||||
type AddedDecodersCount = usize;
|
||||
type RemovedDecodersCount = usize;
|
||||
type DecoderPath = String;
|
||||
|
||||
mod component_manager;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Store {
|
||||
waveform: Mutex<Option<Waveform>>,
|
||||
waveform: RwLock<Option<Waveform>>,
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
|
@ -30,7 +36,7 @@ async fn pick_and_load_waveform(
|
|||
let Ok(waveform) = waveform else {
|
||||
panic!("Waveform file reading failed")
|
||||
};
|
||||
*store.waveform.lock().unwrap() = Some(waveform);
|
||||
*store.waveform.write().await = Some(waveform);
|
||||
Ok(Some(file_response.name.unwrap()))
|
||||
}
|
||||
|
||||
|
@ -48,8 +54,9 @@ async fn load_file_with_selected_vars(app: tauri::AppHandle) -> Result<Option<Ja
|
|||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn get_hierarchy(store: tauri::State<'_, Store>) -> Result<serde_json::Value, ()> {
|
||||
let waveform = store.waveform.lock().unwrap();
|
||||
let hierarchy = waveform.as_ref().unwrap().hierarchy();
|
||||
let waveform_lock = store.waveform.read().await;
|
||||
let waveform = waveform_lock.as_ref().unwrap();
|
||||
let hierarchy = waveform.hierarchy();
|
||||
Ok(serde_json::to_value(hierarchy).unwrap())
|
||||
}
|
||||
|
||||
|
@ -65,7 +72,7 @@ async fn load_signal_and_get_timeline(
|
|||
) -> Result<serde_json::Value, ()> {
|
||||
// @TODO run (all?) in a blocking thread?
|
||||
let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap();
|
||||
let mut waveform_lock = store.waveform.lock().unwrap();
|
||||
let mut waveform_lock = store.waveform.write().await;
|
||||
let waveform = waveform_lock.as_mut().unwrap();
|
||||
waveform.load_signals_multi_threaded(&[signal_ref]);
|
||||
let signal = waveform.get_signal(signal_ref).unwrap();
|
||||
|
@ -78,19 +85,56 @@ async fn load_signal_and_get_timeline(
|
|||
timeline_viewport_x,
|
||||
block_height,
|
||||
var_format,
|
||||
);
|
||||
|mut value: String| {
|
||||
Box::pin(async {
|
||||
// We need to spawn a (non-runtime-specific?) blocking task before calling component methods to prevent this error:
|
||||
// "Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks."
|
||||
// @TODO Workaround? Is it a problem only for non-Rust components? Is it needed only when there is a problem in the component (e.g. "`Err` value: wasm trap: cannot enter component instance"?)
|
||||
// let value = std::thread::spawn(move || {
|
||||
// futures::executor::block_on(async move {
|
||||
let decoders = component_manager::DECODERS.read().await;
|
||||
let mut store_lock = component_manager::STORE.lock().await;
|
||||
let mut store = store_lock.as_context_mut();
|
||||
|
||||
for decoder in decoders.iter() {
|
||||
value = decoder
|
||||
.component_decoder_decoder()
|
||||
.call_format_signal_value(&mut store, &value)
|
||||
// @TODO Resolve panic when running non-Rust components:
|
||||
// `Err` value: wasm trap: cannot enter component instance
|
||||
// https://github.com/bytecodealliance/wasmtime/issues/8670 ?
|
||||
.unwrap()
|
||||
}
|
||||
// value
|
||||
// })
|
||||
// }).join().unwrap();
|
||||
value
|
||||
})
|
||||
},
|
||||
)
|
||||
.await;
|
||||
Ok(serde_json::to_value(timeline).unwrap())
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn unload_signal(signal_ref_index: usize, store: tauri::State<'_, Store>) -> Result<(), ()> {
|
||||
let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap();
|
||||
let mut waveform_lock = store.waveform.lock().unwrap();
|
||||
let mut waveform_lock = store.waveform.write().await;
|
||||
let waveform = waveform_lock.as_mut().unwrap();
|
||||
waveform.unload_signals(&[signal_ref]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn add_decoders(decoder_paths: Vec<DecoderPath>) -> Result<AddedDecodersCount, ()> {
|
||||
Ok(component_manager::add_decoders(decoder_paths).await)
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn remove_all_decoders() -> Result<RemovedDecodersCount, ()> {
|
||||
Ok(component_manager::remove_all_decoders().await)
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
// https://github.com/tauri-apps/tauri/issues/8462
|
||||
|
@ -109,6 +153,8 @@ pub fn run() {
|
|||
get_hierarchy,
|
||||
load_signal_and_get_timeline,
|
||||
unload_signal,
|
||||
add_decoders,
|
||||
remove_all_decoders,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
|
16
src-tauri/wit/world.wit
Normal file
16
src-tauri/wit/world.wit
Normal file
|
@ -0,0 +1,16 @@
|
|||
package component:decoder;
|
||||
|
||||
interface host {
|
||||
log: func(message: string);
|
||||
}
|
||||
|
||||
interface decoder {
|
||||
init: func();
|
||||
name: func() -> string;
|
||||
format-signal-value: func(value: string) -> string;
|
||||
}
|
||||
|
||||
world component {
|
||||
import host;
|
||||
export decoder;
|
||||
}
|
1
test_files/components/README.md
Normal file
1
test_files/components/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
https://component-model.bytecodealliance.org/
|
1
test_files/components/javascript_decoder/.gitignore
vendored
Normal file
1
test_files/components/javascript_decoder/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
10
test_files/components/javascript_decoder/README.md
Normal file
10
test_files/components/javascript_decoder/README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
How to create and build the Javascript component:
|
||||
|
||||
1. Create the `javascript_decoder` folder
|
||||
2. `cd javascript_decoder`
|
||||
3. Create `.gitignore` with content `node_modules`
|
||||
4. `npm install @bytecodealliance/jco @bytecodealliance/componentize-js binaryen`
|
||||
5. Create the `src` folder with the file `index.js`
|
||||
6. Create the `wit` folder with the file `world.wit`
|
||||
7. Update code as needed
|
||||
8. `npx jco componentize src/index.js --wit wit/world.wit --out javascript_decoder.wasm && npx jco opt javascript_decoder.wasm --output javascript_decoder.wasm`
|
BIN
test_files/components/javascript_decoder/javascript_decoder.wasm
Normal file
BIN
test_files/components/javascript_decoder/javascript_decoder.wasm
Normal file
Binary file not shown.
570
test_files/components/javascript_decoder/package-lock.json
generated
Normal file
570
test_files/components/javascript_decoder/package-lock.json
generated
Normal file
|
@ -0,0 +1,570 @@
|
|||
{
|
||||
"name": "javascript_decoder",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@bytecodealliance/componentize-js": "^0.8.3",
|
||||
"@bytecodealliance/jco": "^1.2.4",
|
||||
"binaryen": "^117.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/componentize-js": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/componentize-js/-/componentize-js-0.8.3.tgz",
|
||||
"integrity": "sha512-QyEHRtVg/Cg6RsA75AvRSbSOr0u+FLuXqB89X4Sys44K/VT5g/S9eMn8gqTotfuXVU3btS3Z4QAiyHSF2bja3w==",
|
||||
"dependencies": {
|
||||
"@bytecodealliance/jco": "1.1.1",
|
||||
"@bytecodealliance/wizer": "^3.0.1",
|
||||
"es-module-lexer": "^1.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/componentize-js/node_modules/@bytecodealliance/jco": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.1.1.tgz",
|
||||
"integrity": "sha512-s8Zz6GFPlo2g+dsGp1OMIWXSZnM4FyIloxNAc4grF5TZwFoD00Gj8b0xvpmFSeZj36X/bJPa7x3za3j7Cfeetw==",
|
||||
"dependencies": {
|
||||
"@bytecodealliance/preview2-shim": "^0.16.1",
|
||||
"binaryen": "^116.0.0",
|
||||
"chalk-template": "^1",
|
||||
"commander": "^12",
|
||||
"mkdirp": "^3",
|
||||
"ora": "^8",
|
||||
"terser": "^5"
|
||||
},
|
||||
"bin": {
|
||||
"jco": "src/jco.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/componentize-js/node_modules/binaryen": {
|
||||
"version": "116.0.0",
|
||||
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0.tgz",
|
||||
"integrity": "sha512-Hp0dXC6Cb/rTwWEoUS2BRghObE7g/S9umKtxuTDt3f61G6fNTE/YVew/ezyy3IdHcLx3f17qfh6LwETgCfvWkQ==",
|
||||
"bin": {
|
||||
"wasm-opt": "bin/wasm-opt",
|
||||
"wasm2js": "bin/wasm2js"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/jco": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.2.4.tgz",
|
||||
"integrity": "sha512-uuOm9UkYqWp5uElYDNzlhjbdrAmczEvETgQdI1hFTk79h+mrmHPRV32pgRP5o1eHVwMIpuk4XQkDIhFbksurUw==",
|
||||
"dependencies": {
|
||||
"@bytecodealliance/preview2-shim": "^0.16.2",
|
||||
"binaryen": "^116.0.0",
|
||||
"chalk-template": "^1",
|
||||
"commander": "^12",
|
||||
"mkdirp": "^3",
|
||||
"ora": "^8",
|
||||
"terser": "^5"
|
||||
},
|
||||
"bin": {
|
||||
"jco": "src/jco.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/jco/node_modules/binaryen": {
|
||||
"version": "116.0.0",
|
||||
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0.tgz",
|
||||
"integrity": "sha512-Hp0dXC6Cb/rTwWEoUS2BRghObE7g/S9umKtxuTDt3f61G6fNTE/YVew/ezyy3IdHcLx3f17qfh6LwETgCfvWkQ==",
|
||||
"bin": {
|
||||
"wasm-opt": "bin/wasm-opt",
|
||||
"wasm2js": "bin/wasm2js"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/preview2-shim": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.16.2.tgz",
|
||||
"integrity": "sha512-36MwesmbLSf3Y5/OHcS85iBaF0N92CQ4gpjtDVKSbrjxmrBKCWlWVfoQ03F/cqDg8k5K7pzVaVBH0XBIbTCfTQ=="
|
||||
},
|
||||
"node_modules/@bytecodealliance/wizer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/wizer/-/wizer-3.0.1.tgz",
|
||||
"integrity": "sha512-f0NBiBHCNBkbFHTPRbA7aKf/t4KyNhi2KvSqw3QzCgi8wFF/uLZ0dhejj93rbiKO/iwWbmU7v9K3SVkW81mcjQ==",
|
||||
"bin": {
|
||||
"wizer": "wizer.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@bytecodealliance/wizer-darwin-arm64": "3.0.1",
|
||||
"@bytecodealliance/wizer-darwin-x64": "3.0.1",
|
||||
"@bytecodealliance/wizer-linux-arm64": "3.0.1",
|
||||
"@bytecodealliance/wizer-linux-s390x": "3.0.1",
|
||||
"@bytecodealliance/wizer-linux-x64": "3.0.1",
|
||||
"@bytecodealliance/wizer-win32-x64": "3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/wizer-darwin-arm64": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-darwin-arm64/-/wizer-darwin-arm64-3.0.1.tgz",
|
||||
"integrity": "sha512-/8KYSajyhO9koAE3qQhYfC6belZheJw9X3XqW7hrizTpj6n4z4OJFhhqwJmiYFUUsPtC7OxcXMFFPbTuSQPBcw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"bin": {
|
||||
"wizer-darwin-arm64": "wizer"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/wizer-darwin-x64": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-darwin-x64/-/wizer-darwin-x64-3.0.1.tgz",
|
||||
"integrity": "sha512-bMReultN/r+W/BRXV0F+28U5dZwbQT/ZO0k4icZlhUhrv5/wpQJix7Z/ZvBnVQ+/JHb0QDUpFk2/zCtgkRXP6Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"bin": {
|
||||
"wizer-darwin-x64": "wizer"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/wizer-linux-arm64": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-arm64/-/wizer-linux-arm64-3.0.1.tgz",
|
||||
"integrity": "sha512-35ZhAeYxWK3bTqqgwysbBWlGlrlMNKNng3ZITQV2PAtafpE7aCeqywl7VAS4lLRG5eTb7wxNgN7zf8d3wiIFTQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"bin": {
|
||||
"wizer-linux-arm64": "wizer"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/wizer-linux-s390x": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-s390x/-/wizer-linux-s390x-3.0.1.tgz",
|
||||
"integrity": "sha512-Smvy9mguEMtX0lupDLTPshXUzAHeOhgscr1bhGNjeCCLD1sd8rIjBvWV19Wtra0BL1zTuU2EPOHjR/4k8WoyDg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"bin": {
|
||||
"wizer-linux-s390x": "wizer"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/wizer-linux-x64": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-x64/-/wizer-linux-x64-3.0.1.tgz",
|
||||
"integrity": "sha512-uUue78xl7iwndsGgTsagHLTLyLBVHhwzuywiwHt1xw8y0X0O8REKRLBoB7+LdM+pttDPdFtKJgbTFL4UPAA7Yw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"bin": {
|
||||
"wizer-linux-x64": "wizer"
|
||||
}
|
||||
},
|
||||
"node_modules/@bytecodealliance/wizer-win32-x64": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-win32-x64/-/wizer-win32-x64-3.0.1.tgz",
|
||||
"integrity": "sha512-ycd38sx1UTZpHZwh8IfH/4N3n0OQUB8awxkUSLXf9PolEd088YbxoPB3noHy4E+L2oYN7KZMrg9517pX0z2RhQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"bin": {
|
||||
"wizer-win32-x64": "wizer"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
||||
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
|
||||
"integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/binaryen": {
|
||||
"version": "117.0.0",
|
||||
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-117.0.0.tgz",
|
||||
"integrity": "sha512-1D+O881OXxY737WPKfIgEscCn3vWGqTsd0m5nGKzvbtadVYw5pZ3eebineH/oV5c/rAW80Bojrsa6firSSIsUw==",
|
||||
"bin": {
|
||||
"wasm-as": "bin/wasm-as",
|
||||
"wasm-ctor-eval": "bin/wasm-ctor-eval",
|
||||
"wasm-dis": "bin/wasm-dis",
|
||||
"wasm-merge": "bin/wasm-merge",
|
||||
"wasm-metadce": "bin/wasm-metadce",
|
||||
"wasm-opt": "bin/wasm-opt",
|
||||
"wasm-reduce": "bin/wasm-reduce",
|
||||
"wasm-shell": "bin/wasm-shell",
|
||||
"wasm2js": "bin/wasm2js"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk-template": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.0.tgz",
|
||||
"integrity": "sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==",
|
||||
"dependencies": {
|
||||
"chalk": "^5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk-template?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-cursor": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
|
||||
"integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
|
||||
"dependencies": {
|
||||
"restore-cursor": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-spinners": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
|
||||
"integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
|
||||
"integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw=="
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
|
||||
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw=="
|
||||
},
|
||||
"node_modules/get-east-asian-width": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz",
|
||||
"integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-interactive": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
|
||||
"integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-unicode-supported": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz",
|
||||
"integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-symbols": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
|
||||
"integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==",
|
||||
"dependencies": {
|
||||
"chalk": "^5.3.0",
|
||||
"is-unicode-supported": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-symbols/node_modules/is-unicode-supported": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
|
||||
"integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-fn": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
|
||||
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
|
||||
"bin": {
|
||||
"mkdirp": "dist/cjs/src/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/onetime": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||
"dependencies": {
|
||||
"mimic-fn": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ora": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ora/-/ora-8.0.1.tgz",
|
||||
"integrity": "sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==",
|
||||
"dependencies": {
|
||||
"chalk": "^5.3.0",
|
||||
"cli-cursor": "^4.0.0",
|
||||
"cli-spinners": "^2.9.2",
|
||||
"is-interactive": "^2.0.0",
|
||||
"is-unicode-supported": "^2.0.0",
|
||||
"log-symbols": "^6.0.0",
|
||||
"stdin-discarder": "^0.2.1",
|
||||
"string-width": "^7.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
|
||||
"integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
|
||||
"dependencies": {
|
||||
"onetime": "^5.1.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stdin-discarder": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
|
||||
"integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz",
|
||||
"integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.31.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.1.tgz",
|
||||
"integrity": "sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
"terser": "bin/terser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
}
|
||||
}
|
||||
}
|
7
test_files/components/javascript_decoder/package.json
Normal file
7
test_files/components/javascript_decoder/package.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@bytecodealliance/componentize-js": "^0.8.3",
|
||||
"@bytecodealliance/jco": "^1.2.4",
|
||||
"binaryen": "^117.0.0"
|
||||
}
|
||||
}
|
17
test_files/components/javascript_decoder/src/index.js
Normal file
17
test_files/components/javascript_decoder/src/index.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { log } from "component:decoder/host"
|
||||
|
||||
const name = "Javascript Test Decoder"
|
||||
|
||||
export const decoder = {
|
||||
init() {
|
||||
log(`${name} initialized`)
|
||||
},
|
||||
name() {
|
||||
return name
|
||||
},
|
||||
formatSignalValue(value) {
|
||||
return value + "!"
|
||||
}
|
||||
}
|
||||
|
||||
|
16
test_files/components/javascript_decoder/wit/world.wit
Normal file
16
test_files/components/javascript_decoder/wit/world.wit
Normal file
|
@ -0,0 +1,16 @@
|
|||
package component:decoder;
|
||||
|
||||
interface host {
|
||||
log: func(message: string);
|
||||
}
|
||||
|
||||
interface decoder {
|
||||
init: func();
|
||||
name: func() -> string;
|
||||
format-signal-value: func(value: string) -> string;
|
||||
}
|
||||
|
||||
world component {
|
||||
import host;
|
||||
export decoder;
|
||||
}
|
1
test_files/components/python_decoder/.gitignore
vendored
Normal file
1
test_files/components/python_decoder/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
__pycache__
|
10
test_files/components/python_decoder/README.md
Normal file
10
test_files/components/python_decoder/README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
How to create and build the Python component:
|
||||
|
||||
1. `pip install componentize-py`
|
||||
2. Create the `python_decoder` folder
|
||||
3. `cd python_decoder`
|
||||
4. Create `.gitignore` with content `__pycache__`
|
||||
5. Create the `src` folder with the file `app.py`
|
||||
6. Create the `wit` folder with the file `world.wit`
|
||||
7. Update code as needed
|
||||
8. `rm -rf src/bindings && componentize-py --wit-path wit/world.wit bindings src/bindings && componentize-py --wit-path wit/world.wit componentize src.app --output python_decoder.wasm`
|
BIN
test_files/components/python_decoder/python_decoder.wasm
Normal file
BIN
test_files/components/python_decoder/python_decoder.wasm
Normal file
Binary file not shown.
17
test_files/components/python_decoder/src/app.py
Normal file
17
test_files/components/python_decoder/src/app.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from .bindings.component import exports
|
||||
from .bindings.component.imports import host
|
||||
|
||||
name = "Python Test Decoder"
|
||||
|
||||
class Decoder(exports.Decoder):
|
||||
def init(self) -> None:
|
||||
# @TODO it panics with error `7: 0xae8683 - libcomponentize_py_runtime.so!componentize-py#Dispatch`
|
||||
# - see https://github.com/bytecodealliance/componentize-py/blob/e20d9e6706ff1421cd8001449acb51eb9c87d0c6/runtime/src/lib.rs#L404
|
||||
# host.log(f"{name} initialized")
|
||||
return None
|
||||
|
||||
def name(self) -> str:
|
||||
return name
|
||||
|
||||
def format_signal_value(self, value: str) -> str:
|
||||
return value + "!"
|
|
@ -0,0 +1,12 @@
|
|||
from typing import TypeVar, Generic, Union, Optional, Protocol, Tuple, List, Any, Self
|
||||
from enum import Flag, Enum, auto
|
||||
from dataclasses import dataclass
|
||||
from abc import abstractmethod
|
||||
import weakref
|
||||
|
||||
from .types import Result, Ok, Err, Some
|
||||
|
||||
|
||||
|
||||
class Component(Protocol):
|
||||
pass
|
|
@ -0,0 +1,24 @@
|
|||
from typing import TypeVar, Generic, Union, Optional, Protocol, Tuple, List, Any, Self
|
||||
from enum import Flag, Enum, auto
|
||||
from dataclasses import dataclass
|
||||
from abc import abstractmethod
|
||||
import weakref
|
||||
|
||||
from ..types import Result, Ok, Err, Some
|
||||
|
||||
|
||||
class Decoder(Protocol):
|
||||
|
||||
@abstractmethod
|
||||
def init(self) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def format_signal_value(self, value: str) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
from typing import TypeVar, Generic, Union, Optional, Protocol, Tuple, List, Any, Self
|
||||
from enum import Flag, Enum, auto
|
||||
from dataclasses import dataclass
|
||||
from abc import abstractmethod
|
||||
import weakref
|
||||
|
||||
from ..types import Result, Ok, Err, Some
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
from typing import TypeVar, Generic, Union, Optional, Protocol, Tuple, List, Any, Self
|
||||
from enum import Flag, Enum, auto
|
||||
from dataclasses import dataclass
|
||||
from abc import abstractmethod
|
||||
import weakref
|
||||
|
||||
from ..types import Result, Ok, Err, Some
|
||||
|
||||
|
||||
|
||||
def log(message: str) -> None:
|
||||
raise NotImplementedError
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from typing import TypeVar, Generic, Union, Optional, Protocol, Tuple, List, Any, Self
|
||||
from enum import Flag, Enum, auto
|
||||
from dataclasses import dataclass
|
||||
from abc import abstractmethod
|
||||
import weakref
|
||||
|
||||
|
||||
S = TypeVar('S')
|
||||
@dataclass
|
||||
class Some(Generic[S]):
|
||||
value: S
|
||||
|
||||
T = TypeVar('T')
|
||||
@dataclass
|
||||
class Ok(Generic[T]):
|
||||
value: T
|
||||
|
||||
E = TypeVar('E')
|
||||
@dataclass(frozen=True)
|
||||
class Err(Generic[E], Exception):
|
||||
value: E
|
||||
|
||||
Result = Union[Ok[T], Err[E]]
|
16
test_files/components/python_decoder/wit/world.wit
Normal file
16
test_files/components/python_decoder/wit/world.wit
Normal file
|
@ -0,0 +1,16 @@
|
|||
package component:decoder;
|
||||
|
||||
interface host {
|
||||
log: func(message: string);
|
||||
}
|
||||
|
||||
interface decoder {
|
||||
init: func();
|
||||
name: func() -> string;
|
||||
format-signal-value: func(value: string) -> string;
|
||||
}
|
||||
|
||||
world component {
|
||||
import host;
|
||||
export decoder;
|
||||
}
|
26
test_files/components/rust_decoder/Cargo.toml
Normal file
26
test_files/components/rust_decoder/Cargo.toml
Normal file
|
@ -0,0 +1,26 @@
|
|||
[package]
|
||||
name = "rust_decoder"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
readme.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
wit-bindgen-rt = { version = "0.26.0", features = ["bitflags"] }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
opt-level = "s"
|
||||
debug = false
|
||||
strip = true
|
||||
lto = true
|
||||
|
||||
[package.metadata.component]
|
||||
package = "component:rust-decoder"
|
||||
|
||||
[package.metadata.component.dependencies]
|
7
test_files/components/rust_decoder/README.md
Normal file
7
test_files/components/rust_decoder/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
How to create and build the Rust component:
|
||||
|
||||
1. `cargo install cargo-component`
|
||||
2. `cargo component new rust_decoder --lib`
|
||||
3. `cd rust_decoder`
|
||||
4. Update code as needed
|
||||
5. `cargo component build --release --target wasm32-unknown-unknown && cp ../../../target/wasm32-unknown-unknown/release/rust_decoder.wasm .`
|
BIN
test_files/components/rust_decoder/rust_decoder.wasm
Normal file
BIN
test_files/components/rust_decoder/rust_decoder.wasm
Normal file
Binary file not shown.
220
test_files/components/rust_decoder/src/bindings.rs
Normal file
220
test_files/components/rust_decoder/src/bindings.rs
Normal file
|
@ -0,0 +1,220 @@
|
|||
// Generated by `wit-bindgen` 0.25.0. DO NOT EDIT!
|
||||
// Options used:
|
||||
#[allow(dead_code)]
|
||||
pub mod component {
|
||||
#[allow(dead_code)]
|
||||
pub mod decoder {
|
||||
#[allow(dead_code, clippy::all)]
|
||||
pub mod host {
|
||||
#[used]
|
||||
#[doc(hidden)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
static __FORCE_SECTION_REF: fn() =
|
||||
super::super::super::__link_custom_section_describing_imports;
|
||||
#[allow(unused_unsafe, clippy::all)]
|
||||
pub fn log(message: &str) {
|
||||
unsafe {
|
||||
let vec0 = message;
|
||||
let ptr0 = vec0.as_ptr().cast::<u8>();
|
||||
let len0 = vec0.len();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[link(wasm_import_module = "component:decoder/host")]
|
||||
extern "C" {
|
||||
#[link_name = "log"]
|
||||
fn wit_import(_: *mut u8, _: usize);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn wit_import(_: *mut u8, _: usize) {
|
||||
unreachable!()
|
||||
}
|
||||
wit_import(ptr0.cast_mut(), len0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub mod exports {
|
||||
#[allow(dead_code)]
|
||||
pub mod component {
|
||||
#[allow(dead_code)]
|
||||
pub mod decoder {
|
||||
#[allow(dead_code, clippy::all)]
|
||||
pub mod decoder {
|
||||
#[used]
|
||||
#[doc(hidden)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
static __FORCE_SECTION_REF: fn() =
|
||||
super::super::super::super::__link_custom_section_describing_imports;
|
||||
use super::super::super::super::_rt;
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn _export_init_cabi<T: Guest>() {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
_rt::run_ctors_once();
|
||||
T::init();
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn _export_name_cabi<T: Guest>() -> *mut u8 {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
_rt::run_ctors_once();
|
||||
let result0 = T::name();
|
||||
let ptr1 = _RET_AREA.0.as_mut_ptr().cast::<u8>();
|
||||
let vec2 = (result0.into_bytes()).into_boxed_slice();
|
||||
let ptr2 = vec2.as_ptr().cast::<u8>();
|
||||
let len2 = vec2.len();
|
||||
::core::mem::forget(vec2);
|
||||
*ptr1.add(4).cast::<usize>() = len2;
|
||||
*ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut();
|
||||
ptr1
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn __post_return_name<T: Guest>(arg0: *mut u8) {
|
||||
let l0 = *arg0.add(0).cast::<*mut u8>();
|
||||
let l1 = *arg0.add(4).cast::<usize>();
|
||||
_rt::cabi_dealloc(l0, l1, 1);
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn _export_format_signal_value_cabi<T: Guest>(
|
||||
arg0: *mut u8,
|
||||
arg1: usize,
|
||||
) -> *mut u8 {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
_rt::run_ctors_once();
|
||||
let len0 = arg1;
|
||||
let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0);
|
||||
let result1 = T::format_signal_value(_rt::string_lift(bytes0));
|
||||
let ptr2 = _RET_AREA.0.as_mut_ptr().cast::<u8>();
|
||||
let vec3 = (result1.into_bytes()).into_boxed_slice();
|
||||
let ptr3 = vec3.as_ptr().cast::<u8>();
|
||||
let len3 = vec3.len();
|
||||
::core::mem::forget(vec3);
|
||||
*ptr2.add(4).cast::<usize>() = len3;
|
||||
*ptr2.add(0).cast::<*mut u8>() = ptr3.cast_mut();
|
||||
ptr2
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn __post_return_format_signal_value<T: Guest>(arg0: *mut u8) {
|
||||
let l0 = *arg0.add(0).cast::<*mut u8>();
|
||||
let l1 = *arg0.add(4).cast::<usize>();
|
||||
_rt::cabi_dealloc(l0, l1, 1);
|
||||
}
|
||||
pub trait Guest {
|
||||
fn init();
|
||||
fn name() -> _rt::String;
|
||||
fn format_signal_value(value: _rt::String) -> _rt::String;
|
||||
}
|
||||
#[doc(hidden)]
|
||||
|
||||
macro_rules! __export_component_decoder_decoder_cabi{
|
||||
($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
|
||||
|
||||
#[export_name = "component:decoder/decoder#init"]
|
||||
unsafe extern "C" fn export_init() {
|
||||
$($path_to_types)*::_export_init_cabi::<$ty>()
|
||||
}
|
||||
#[export_name = "component:decoder/decoder#name"]
|
||||
unsafe extern "C" fn export_name() -> *mut u8 {
|
||||
$($path_to_types)*::_export_name_cabi::<$ty>()
|
||||
}
|
||||
#[export_name = "cabi_post_component:decoder/decoder#name"]
|
||||
unsafe extern "C" fn _post_return_name(arg0: *mut u8,) {
|
||||
$($path_to_types)*::__post_return_name::<$ty>(arg0)
|
||||
}
|
||||
#[export_name = "component:decoder/decoder#format-signal-value"]
|
||||
unsafe extern "C" fn export_format_signal_value(arg0: *mut u8,arg1: usize,) -> *mut u8 {
|
||||
$($path_to_types)*::_export_format_signal_value_cabi::<$ty>(arg0, arg1)
|
||||
}
|
||||
#[export_name = "cabi_post_component:decoder/decoder#format-signal-value"]
|
||||
unsafe extern "C" fn _post_return_format_signal_value(arg0: *mut u8,) {
|
||||
$($path_to_types)*::__post_return_format_signal_value::<$ty>(arg0)
|
||||
}
|
||||
};);
|
||||
}
|
||||
#[doc(hidden)]
|
||||
pub(crate) use __export_component_decoder_decoder_cabi;
|
||||
#[repr(align(4))]
|
||||
struct _RetArea([::core::mem::MaybeUninit<u8>; 8]);
|
||||
static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); 8]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mod _rt {
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn run_ctors_once() {
|
||||
wit_bindgen_rt::run_ctors_once();
|
||||
}
|
||||
pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) {
|
||||
if size == 0 {
|
||||
return;
|
||||
}
|
||||
let layout = alloc::Layout::from_size_align_unchecked(size, align);
|
||||
alloc::dealloc(ptr as *mut u8, layout);
|
||||
}
|
||||
pub use alloc_crate::string::String;
|
||||
pub use alloc_crate::vec::Vec;
|
||||
pub unsafe fn string_lift(bytes: Vec<u8>) -> String {
|
||||
if cfg!(debug_assertions) {
|
||||
String::from_utf8(bytes).unwrap()
|
||||
} else {
|
||||
String::from_utf8_unchecked(bytes)
|
||||
}
|
||||
}
|
||||
pub use alloc_crate::alloc;
|
||||
extern crate alloc as alloc_crate;
|
||||
}
|
||||
|
||||
/// Generates `#[no_mangle]` functions to export the specified type as the
|
||||
/// root implementation of all generated traits.
|
||||
///
|
||||
/// For more information see the documentation of `wit_bindgen::generate!`.
|
||||
///
|
||||
/// ```rust
|
||||
/// # macro_rules! export{ ($($t:tt)*) => (); }
|
||||
/// # trait Guest {}
|
||||
/// struct MyType;
|
||||
///
|
||||
/// impl Guest for MyType {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// export!(MyType);
|
||||
/// ```
|
||||
#[allow(unused_macros)]
|
||||
#[doc(hidden)]
|
||||
|
||||
macro_rules! __export_component_impl {
|
||||
($ty:ident) => (self::export!($ty with_types_in self););
|
||||
($ty:ident with_types_in $($path_to_types_root:tt)*) => (
|
||||
$($path_to_types_root)*::exports::component::decoder::decoder::__export_component_decoder_decoder_cabi!($ty with_types_in $($path_to_types_root)*::exports::component::decoder::decoder);
|
||||
)
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub(crate) use __export_component_impl as export;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[link_section = "component-type:wit-bindgen:0.25.0:component:encoded world"]
|
||||
#[doc(hidden)]
|
||||
pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 315] = *b"\
|
||||
\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xbb\x01\x01A\x02\x01\
|
||||
A\x04\x01B\x02\x01@\x01\x07messages\x01\0\x04\0\x03log\x01\0\x03\x01\x16componen\
|
||||
t:decoder/host\x05\0\x01B\x06\x01@\0\x01\0\x04\0\x04init\x01\0\x01@\0\0s\x04\0\x04\
|
||||
name\x01\x01\x01@\x01\x05values\0s\x04\0\x13format-signal-value\x01\x02\x04\x01\x19\
|
||||
component:decoder/decoder\x05\x01\x04\x01\x1bcomponent:decoder/component\x04\0\x0b\
|
||||
\x0f\x01\0\x09component\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-c\
|
||||
omponent\x070.208.1\x10wit-bindgen-rust\x060.25.0";
|
||||
|
||||
#[inline(never)]
|
||||
#[doc(hidden)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn __link_custom_section_describing_imports() {
|
||||
wit_bindgen_rt::maybe_link_cabi_realloc();
|
||||
}
|
30
test_files/components/rust_decoder/src/lib.rs
Normal file
30
test_files/components/rust_decoder/src/lib.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
#[allow(warnings)]
|
||||
mod bindings;
|
||||
|
||||
use bindings::component::decoder::host;
|
||||
use bindings::exports::component::decoder::decoder;
|
||||
|
||||
macro_rules! log {
|
||||
($($arg:tt)*) => (host::log(&format!($($arg)*)))
|
||||
}
|
||||
|
||||
static NAME: &str = "Rust Test Decoder";
|
||||
|
||||
struct Component;
|
||||
|
||||
impl decoder::Guest for Component {
|
||||
fn init() {
|
||||
log!("'{NAME}' initialized")
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
NAME.to_string()
|
||||
}
|
||||
|
||||
fn format_signal_value(mut value: String) -> String {
|
||||
value.push('!');
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
bindings::export!(Component with_types_in bindings);
|
16
test_files/components/rust_decoder/wit/world.wit
Normal file
16
test_files/components/rust_decoder/wit/world.wit
Normal file
|
@ -0,0 +1,16 @@
|
|||
package component:decoder;
|
||||
|
||||
interface host {
|
||||
log: func(message: string);
|
||||
}
|
||||
|
||||
interface decoder {
|
||||
init: func();
|
||||
name: func() -> string;
|
||||
format-signal-value: func(value: string) -> string;
|
||||
}
|
||||
|
||||
world component {
|
||||
import host;
|
||||
export decoder;
|
||||
}
|
Loading…
Reference in a new issue