1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 13:21:38 +03:00

deps: update metrics to a more recent version

This isn't the latest version of metrics; it's just a more recent
version that allows us to remove a duplicate ahash dependency from the
build graph.
This commit is contained in:
Wez Furlong 2024-05-13 09:17:23 -07:00
parent 14d426fea8
commit 281b6e2740
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
12 changed files with 186 additions and 119 deletions

36
Cargo.lock generated
View File

@ -23,17 +23,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "ahash"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.8.11" version = "0.8.11"
@ -2348,7 +2337,7 @@ version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash",
"allocator-api2", "allocator-api2",
] ]
@ -2898,7 +2887,7 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
name = "lfucache" name = "lfucache"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash",
"config", "config",
"fnv", "fnv",
"intrusive-collections", "intrusive-collections",
@ -3295,25 +3284,24 @@ dependencies = [
[[package]] [[package]]
name = "metrics" name = "metrics"
version = "0.17.1" version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55586aa936c35f34ba8aa5d97356d554311206e1ce1f9e68fe7b07288e5ad827" checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5"
dependencies = [ dependencies = [
"ahash 0.7.8", "ahash",
"metrics-macros", "metrics-macros",
"portable-atomic",
] ]
[[package]] [[package]]
name = "metrics-macros" name = "metrics-macros"
version = "0.4.1" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0daa0ab3a0ae956d0e2c1f42511422850e577d36a255357d1a7d08d45ee3a2f1" checksum = "38b4faf00617defe497754acde3024865bc143d44a86799b24e191ecff91354f"
dependencies = [ dependencies = [
"lazy_static",
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "syn 2.0.63",
"syn 1.0.109",
] ]
[[package]] [[package]]
@ -4196,6 +4184,12 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]] [[package]]
name = "portable-pty" name = "portable-pty"
version = "0.8.1" version = "0.8.1"

View File

@ -12,7 +12,7 @@ anyhow = "1.0"
config = { path = "../config" } config = { path = "../config" }
leb128 = "0.2" leb128 = "0.2"
log = "0.4" log = "0.4"
metrics = { version="0.17", features=["std"]} metrics = "0.21"
mux = { path = "../mux" } mux = { path = "../mux" }
portable-pty = { path = "../pty", features = ["serde_support"]} portable-pty = { path = "../pty", features = ["serde_support"]}
rangeset = { path = "../rangeset" } rangeset = { path = "../rangeset" }

View File

@ -11,7 +11,7 @@ ahash = "0.8"
config = { path = "../config" } config = { path = "../config" }
fnv = "1.0" fnv = "1.0"
intrusive-collections = "0.9" intrusive-collections = "0.9"
metrics = { version="0.17", features=["std"]} metrics = "0.21"
[dev-dependencies] [dev-dependencies]
k9 = "0.12" k9 = "0.12"

View File

@ -18,8 +18,8 @@ use deltae::LabValue;
use image::Pixel; use image::Pixel;
use lru::LruCache; use lru::LruCache;
use luahelper::impl_lua_conversion_dynamic; use luahelper::impl_lua_conversion_dynamic;
use std::num::NonZeroUsize;
use std::collections::HashMap; use std::collections::HashMap;
use std::num::NonZeroUsize;
use std::sync::Mutex; use std::sync::Mutex;
use std::time::SystemTime; use std::time::SystemTime;
use wezterm_dynamic::{FromDynamic, ToDynamic}; use wezterm_dynamic::{FromDynamic, ToDynamic};

View File

@ -25,7 +25,7 @@ lazy_static = "1.4"
libc = "0.2" libc = "0.2"
log = "0.4" log = "0.4"
luahelper = { path = "../luahelper" } luahelper = { path = "../luahelper" }
metrics = { version="0.17", features=["std"]} metrics = "0.21"
mlua = "0.9" mlua = "0.9"
names = { version = "0.12", default-features = false } names = { version = "0.12", default-features = false }
nix = {version="0.25", features=["term"]} nix = {version="0.25", features=["term"]}

View File

@ -5,10 +5,10 @@ use super::*;
use crate::color::{ColorPalette, RgbColor}; use crate::color::{ColorPalette, RgbColor};
use crate::config::{BidiMode, NewlineCanon}; use crate::config::{BidiMode, NewlineCanon};
use log::debug; use log::debug;
use std::num::NonZeroUsize;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{BufWriter, Write}; use std::io::{BufWriter, Write};
use std::num::NonZeroUsize;
use std::sync::mpsc::{channel, Sender}; use std::sync::mpsc::{channel, Sender};
use std::sync::Arc; use std::sync::Arc;
use terminfo::{Database, Value}; use terminfo::{Database, Value};

View File

@ -19,7 +19,7 @@ lazy_static = "1.4"
log = "0.4" log = "0.4"
libc = "0.2" libc = "0.2"
lru = "0.12" lru = "0.12"
metrics = { version="0.17", features=["std"]} metrics = "0.21"
mux = { path = "../mux" } mux = { path = "../mux" }
openssl = "0.10.57" openssl = "0.10.57"
parking_lot = "0.12" parking_lot = "0.12"

View File

@ -1,5 +1,4 @@
use crate::domain::ClientInner; use crate::domain::ClientInner;
use std::num::NonZeroUsize;
use crate::pane::clientpane::ClientPane; use crate::pane::clientpane::ClientPane;
use anyhow::anyhow; use anyhow::anyhow;
use codec::*; use codec::*;
@ -13,6 +12,7 @@ use rangeset::*;
use ratelim::RateLimiter; use ratelim::RateLimiter;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::num::NonZeroUsize;
use std::ops::Range; use std::ops::Range;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -105,7 +105,9 @@ impl RenderableInner {
poll_interval: BASE_POLL_INTERVAL, poll_interval: BASE_POLL_INTERVAL,
cursor_position: StableCursorPosition::default(), cursor_position: StableCursorPosition::default(),
dimensions, dimensions,
lines: LruCache::new(NonZeroUsize::new(configuration().scrollback_lines.max(128)).unwrap()), lines: LruCache::new(
NonZeroUsize::new(configuration().scrollback_lines.max(128)).unwrap(),
),
title: title.to_string(), title: title.to_string(),
working_dir: None, working_dir: None,
fetch_limiter, fetch_limiter,

View File

@ -28,7 +28,7 @@ lazy_static = "1.4"
lfucache = { path = "../lfucache" } lfucache = { path = "../lfucache" }
log = "0.4" log = "0.4"
memmap2 = "0.2" memmap2 = "0.2"
metrics = { version="0.17", features=["std"]} metrics = "0.21"
ordered-float = "4.1" ordered-float = "4.1"
rangeset = { path = "../rangeset" } rangeset = { path = "../rangeset" }
termwiz = { path = "../termwiz" } termwiz = { path = "../termwiz" }

View File

@ -62,7 +62,7 @@ libc = "0.2"
lfucache = { path = "../lfucache" } lfucache = { path = "../lfucache" }
log = "0.4" log = "0.4"
luahelper = { path = "../luahelper" } luahelper = { path = "../luahelper" }
metrics = { version="0.17", features=["std"]} metrics = "0.21"
mlua = {version="0.9", features=["send"]} mlua = {version="0.9", features=["send"]}
mux = { path = "../mux" } mux = { path = "../mux" }
mux-lua = { path = "../lua-api-crates/mux" } mux-lua = { path = "../lua-api-crates/mux" }

View File

@ -2,10 +2,11 @@ use config::configuration;
use config::lua::get_or_create_sub_module; use config::lua::get_or_create_sub_module;
use config::lua::mlua::Lua; use config::lua::mlua::Lua;
use hdrhistogram::Histogram; use hdrhistogram::Histogram;
use metrics::{GaugeValue, Key, Recorder, Unit}; use metrics::{Counter, Gauge, Key, KeyName, Recorder, SharedString, Unit};
use parking_lot::Mutex;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tabout::{tabulate_output, Alignment, Column}; use tabout::{tabulate_output, Alignment, Column};
@ -14,21 +15,40 @@ lazy_static::lazy_static! {
static ref INNER: Arc<Mutex<Inner>> = make_inner(); static ref INNER: Arc<Mutex<Inner>> = make_inner();
} }
struct Throughput { struct ThroughputInner {
hist: Histogram<u64>, hist: Histogram<u64>,
last: Option<Instant>, last: Option<Instant>,
count: u64, count: u64,
} }
struct Throughput {
inner: Mutex<ThroughputInner>,
}
impl Throughput { impl Throughput {
fn new() -> Self { fn new() -> Self {
Self { Self {
inner: Mutex::new(ThroughputInner {
hist: Histogram::new(2).expect("failed to create histogram"), hist: Histogram::new(2).expect("failed to create histogram"),
last: None, last: None,
count: 0, count: 0,
}),
} }
} }
fn current(&self) -> u64 {
self.inner.lock().current()
}
fn percentiles(&self) -> (u64, u64, u64) {
let inner = self.inner.lock();
let p50 = inner.hist.value_at_percentile(50.);
let p75 = inner.hist.value_at_percentile(75.);
let p95 = inner.hist.value_at_percentile(95.);
(p50, p75, p95)
}
}
impl ThroughputInner {
fn add(&mut self, value: u64) { fn add(&mut self, value: u64) {
if let Some(ref last) = self.last { if let Some(ref last) = self.last {
let elapsed = last.elapsed(); let elapsed = last.elapsed();
@ -57,14 +77,69 @@ impl Throughput {
} }
} }
impl metrics::HistogramFn for Throughput {
fn record(&self, value: f64) {
self.inner.lock().add(value as u64);
}
}
struct ScaledHistogram {
hist: Mutex<Histogram<u64>>,
scale: f64,
}
impl ScaledHistogram {
fn new(scale: f64) -> Arc<Self> {
Arc::new(Self {
hist: Mutex::new(Histogram::new(2).expect("failed to create new Histogram")),
scale,
})
}
fn percentiles(&self) -> (u64, u64, u64) {
let hist = self.hist.lock();
let p50 = hist.value_at_percentile(50.);
let p75 = hist.value_at_percentile(75.);
let p95 = hist.value_at_percentile(95.);
(p50, p75, p95)
}
fn latency_percentiles(&self) -> (Duration, Duration, Duration) {
let hist = self.hist.lock();
let p50 = pctile_latency(&*hist, 50.);
let p75 = pctile_latency(&*hist, 75.);
let p95 = pctile_latency(&*hist, 95.);
(p50, p75, p95)
}
}
impl metrics::HistogramFn for ScaledHistogram {
fn record(&self, value: f64) {
self.hist.lock().record((value * self.scale) as u64).ok();
}
}
fn pctile_latency(histogram: &Histogram<u64>, p: f64) -> Duration { fn pctile_latency(histogram: &Histogram<u64>, p: f64) -> Duration {
Duration::from_nanos(histogram.value_at_percentile(p)) Duration::from_nanos(histogram.value_at_percentile(p))
} }
struct MyCounter {
value: AtomicUsize,
}
impl metrics::CounterFn for MyCounter {
fn increment(&self, value: u64) {
self.value.fetch_add(value as usize, Ordering::Relaxed);
}
fn absolute(&self, value: u64) {
self.value.store(value as usize, Ordering::Relaxed);
}
}
struct Inner { struct Inner {
histograms: HashMap<Key, Histogram<u64>>, histograms: HashMap<Key, Arc<ScaledHistogram>>,
throughput: HashMap<Key, Throughput>, throughput: HashMap<Key, Arc<Throughput>>,
counters: HashMap<Key, u64>, counters: HashMap<Key, Arc<MyCounter>>,
} }
impl Inner { impl Inner {
@ -136,12 +211,10 @@ impl Inner {
if last_print.elapsed() >= Duration::from_secs(seconds) { if last_print.elapsed() >= Duration::from_secs(seconds) {
let mut data = vec![]; let mut data = vec![];
let mut inner = inner.lock().unwrap(); let mut inner = inner.lock();
for (key, tput) in &mut inner.throughput { for (key, tput) in &mut inner.throughput {
let current = tput.current(); let current = tput.current();
let p50 = tput.hist.value_at_percentile(50.); let (p50, p75, p95) = tput.percentiles();
let p75 = tput.hist.value_at_percentile(75.);
let p95 = tput.hist.value_at_percentile(95.);
data.push(vec![ data.push(vec![
key.to_string(), key.to_string(),
format!("{:.2?}", current), format!("{:.2?}", current),
@ -157,9 +230,7 @@ impl Inner {
data.clear(); data.clear();
for (key, histogram) in &inner.histograms { for (key, histogram) in &inner.histograms {
if key.name().ends_with(".size") { if key.name().ends_with(".size") {
let p50 = histogram.value_at_percentile(50.); let (p50, p75, p95) = histogram.percentiles();
let p75 = histogram.value_at_percentile(75.);
let p95 = histogram.value_at_percentile(95.);
data.push(vec![ data.push(vec![
key.to_string(), key.to_string(),
format!("{:.2?}", p50), format!("{:.2?}", p50),
@ -167,9 +238,7 @@ impl Inner {
format!("{:.2?}", p95), format!("{:.2?}", p95),
]); ]);
} else { } else {
let p50 = pctile_latency(histogram, 50.); let (p50, p75, p95) = histogram.latency_percentiles();
let p75 = pctile_latency(histogram, 75.);
let p95 = pctile_latency(histogram, 95.);
data.push(vec![ data.push(vec![
key.to_string(), key.to_string(),
format!("{:.2?}", p50), format!("{:.2?}", p50),
@ -184,7 +253,10 @@ impl Inner {
data.clear(); data.clear();
for (key, count) in &inner.counters { for (key, count) in &inner.counters {
data.push(vec![key.to_string(), count.to_string()]); data.push(vec![
key.to_string(),
count.value.load(Ordering::Relaxed).to_string(),
]);
} }
data.sort_by(|a, b| a[0].cmp(&b[0])); data.sort_by(|a, b| a[0].cmp(&b[0]));
eprintln!(); eprintln!();
@ -226,54 +298,59 @@ impl Stats {
} }
impl Recorder for Stats { impl Recorder for Stats {
fn register_counter( fn describe_counter(&self, _key: KeyName, _unit: Option<Unit>, _description: SharedString) {}
&self,
_key: &Key, fn describe_gauge(&self, _key: KeyName, _unit: Option<Unit>, _description: SharedString) {}
_unit: Option<Unit>,
_description: Option<&'static str>, fn describe_histogram(&self, _key: KeyName, _unit: Option<Unit>, _description: SharedString) {}
) {
fn register_counter(&self, key: &Key) -> Counter {
let mut inner = self.inner.lock();
match inner.counters.get(key) {
Some(existing) => Counter::from_arc(existing.clone()),
None => {
let counter = Arc::new(MyCounter {
value: AtomicUsize::new(0),
});
inner.counters.insert(key.clone(), counter.clone());
metrics::Counter::from_arc(counter)
}
}
} }
fn register_gauge(&self, _key: &Key, _unit: Option<Unit>, _description: Option<&'static str>) {} fn register_gauge(&self, _key: &Key) -> Gauge {
Gauge::noop()
fn register_histogram(
&self,
_key: &Key,
_unit: Option<Unit>,
_description: Option<&'static str>,
) {
} }
fn increment_counter(&self, key: &Key, value: u64) { fn register_histogram(&self, key: &Key) -> metrics::Histogram {
let mut inner = self.inner.lock().unwrap(); let mut inner = self.inner.lock();
let counter = inner.counters.entry(key.clone()).or_insert_with(|| 0);
*counter = *counter + value;
}
fn update_gauge(&self, key: &Key, value: GaugeValue) {
log::trace!("gauge '{}' -> {:?}", key, value);
}
fn record_histogram(&self, key: &Key, value: f64) {
let mut inner = self.inner.lock().unwrap();
if key.name().ends_with(".rate") { if key.name().ends_with(".rate") {
let tput = inner match inner.throughput.get(key) {
.throughput Some(existing) => metrics::Histogram::from_arc(existing.clone()),
.entry(key.clone()) None => {
.or_insert_with(|| Throughput::new()); let tput = Arc::new(Throughput::new());
tput.add(value as u64); inner.throughput.insert(key.clone(), tput.clone());
metrics::Histogram::from_arc(tput)
}
}
} else { } else {
let value = if key.name().ends_with(".size") { match inner.histograms.get(key) {
value Some(existing) => metrics::Histogram::from_arc(existing.clone()),
None => {
let scale = if key.name().ends_with(".size") {
1.0
} else { } else {
// Assume seconds; convert to nanoseconds // Assume seconds; convert to nanoseconds
value * 1_000_000_000.0 1_000_000_000.0
}; };
let histogram = inner
.histograms let histogram = ScaledHistogram::new(scale);
.entry(key.clone()) inner.histograms.insert(key.clone(), histogram.clone());
.or_insert_with(|| Histogram::new(2).expect("failed to crate new Histogram"));
histogram.record(value as u64).ok(); metrics::Histogram::from_arc(histogram)
}
}
} }
} }
} }
@ -283,11 +360,11 @@ pub fn register(lua: &Lua) -> anyhow::Result<()> {
metrics_mod.set( metrics_mod.set(
"get_counters", "get_counters",
lua.create_function(|_, _: ()| { lua.create_function(|_, _: ()| {
let inner = INNER.lock().unwrap(); let inner = INNER.lock();
let counters: HashMap<String, u64> = inner let counters: HashMap<String, usize> = inner
.counters .counters
.iter() .iter()
.map(|(k, &v)| (k.name().to_string(), v)) .map(|(k, v)| (k.name().to_string(), v.value.load(Ordering::Relaxed)))
.collect(); .collect();
Ok(counters) Ok(counters)
})?, })?,
@ -295,16 +372,17 @@ pub fn register(lua: &Lua) -> anyhow::Result<()> {
metrics_mod.set( metrics_mod.set(
"get_throughput", "get_throughput",
lua.create_function(|_, _: ()| { lua.create_function(|_, _: ()| {
let mut inner = INNER.lock().unwrap(); let mut inner = INNER.lock();
let counters: HashMap<String, HashMap<String, u64>> = inner let counters: HashMap<String, HashMap<String, u64>> = inner
.throughput .throughput
.iter_mut() .iter_mut()
.map(|(k, tput)| { .map(|(k, tput)| {
let mut res = HashMap::new(); let mut res = HashMap::new();
res.insert("current".to_string(), tput.current()); res.insert("current".to_string(), tput.current());
res.insert("p50".to_string(), tput.hist.value_at_percentile(50.)); let (p50, p75, p95) = tput.percentiles();
res.insert("p75".to_string(), tput.hist.value_at_percentile(75.)); res.insert("p50".to_string(), p50);
res.insert("p95".to_string(), tput.hist.value_at_percentile(95.)); res.insert("p75".to_string(), p75);
res.insert("p95".to_string(), p95);
(k.name().to_string(), res) (k.name().to_string(), res)
}) })
.collect(); .collect();
@ -314,16 +392,17 @@ pub fn register(lua: &Lua) -> anyhow::Result<()> {
metrics_mod.set( metrics_mod.set(
"get_sizes", "get_sizes",
lua.create_function(|_, _: ()| { lua.create_function(|_, _: ()| {
let mut inner = INNER.lock().unwrap(); let mut inner = INNER.lock();
let counters: HashMap<String, HashMap<String, u64>> = inner let counters: HashMap<String, HashMap<String, u64>> = inner
.histograms .histograms
.iter_mut() .iter_mut()
.filter_map(|(key, hist)| { .filter_map(|(key, hist)| {
if key.name().ends_with(".size") { if key.name().ends_with(".size") {
let mut res = HashMap::new(); let mut res = HashMap::new();
res.insert("p50".to_string(), hist.value_at_percentile(50.)); let (p50, p75, p95) = hist.percentiles();
res.insert("p75".to_string(), hist.value_at_percentile(75.)); res.insert("p50".to_string(), p50);
res.insert("p95".to_string(), hist.value_at_percentile(95.)); res.insert("p75".to_string(), p75);
res.insert("p95".to_string(), p95);
Some((key.name().to_string(), res)) Some((key.name().to_string(), res))
} else { } else {
None None
@ -336,25 +415,17 @@ pub fn register(lua: &Lua) -> anyhow::Result<()> {
metrics_mod.set( metrics_mod.set(
"get_latency", "get_latency",
lua.create_function(|_, _: ()| { lua.create_function(|_, _: ()| {
let mut inner = INNER.lock().unwrap(); let mut inner = INNER.lock();
let counters: HashMap<String, HashMap<String, String>> = inner let counters: HashMap<String, HashMap<String, String>> = inner
.histograms .histograms
.iter_mut() .iter_mut()
.filter_map(|(key, hist)| { .filter_map(|(key, hist)| {
if !key.name().ends_with(".size") { if !key.name().ends_with(".size") {
let mut res = HashMap::new(); let mut res = HashMap::new();
res.insert( let (p50, p75, p95) = hist.latency_percentiles();
"p50".to_string(), res.insert("p50".to_string(), format!("{p50:?}"));
format!("{:?}", pctile_latency(hist, 50.)), res.insert("p75".to_string(), format!("{p75:?}"));
); res.insert("p95".to_string(), format!("{p95:?}"));
res.insert(
"p75".to_string(),
format!("{:?}", pctile_latency(hist, 75.)),
);
res.insert(
"p95".to_string(),
format!("{:?}", pctile_latency(hist, 95.)),
);
Some((key.name().to_string(), res)) Some((key.name().to_string(), res))
} else { } else {
None None

View File

@ -34,7 +34,7 @@ lazy_static = "1.4"
libloading = "0.6" libloading = "0.6"
line_drawing = "0.8" line_drawing = "0.8"
log = "0.4" log = "0.4"
metrics = { version="0.17", features=["std"]} metrics = "0.21"
promise = { path = "../promise" } promise = { path = "../promise" }
raw-window-handle = "0.5" raw-window-handle = "0.5"
resize = "0.5" resize = "0.5"