tinymist_package/registry/
browser.rs1use std::{io::Read, path::Path};
4
5use js_sys::Uint8Array;
6use typst::diag::{EcoString, eco_format};
7use wasm_bindgen::{JsValue, prelude::*};
8
9use super::{PackageError, PackageRegistry, PackageSpec};
10
11#[wasm_bindgen]
13#[derive(Debug, Clone)]
14pub struct ProxyContext {
15 context: JsValue,
16}
17
18#[wasm_bindgen]
19impl ProxyContext {
20 #[wasm_bindgen(constructor)]
22 pub fn new(context: JsValue) -> Self {
23 Self { context }
24 }
25
26 #[wasm_bindgen(getter)]
28 pub fn context(&self) -> JsValue {
29 self.context.clone()
30 }
31
32 pub fn untar(&self, data: &[u8], cb: js_sys::Function) -> Result<(), JsValue> {
35 let cb = move |key: String, value: &[u8], mtime: u64| -> Result<(), JsValue> {
36 let key = JsValue::from_str(&key);
37 let value = Uint8Array::from(value);
38 let mtime = JsValue::from_f64(mtime as f64);
39 cb.call3(&self.context, &key, &value, &mtime).map(|_| ())
40 };
41
42 let decompressed = flate2::read::GzDecoder::new(data);
43 let mut reader = tar::Archive::new(decompressed);
44 let entries = reader.entries();
45 let entries = entries.map_err(|err| {
46 let t = PackageError::MalformedArchive(Some(eco_format!("{err}")));
47 JsValue::from_str(&format!("{t:?}"))
48 })?;
49
50 let mut buf = Vec::with_capacity(1024);
51 for entry in entries {
52 let mut entry = entry.map_err(|e| format!("{e:?}"))?;
54 let header = entry.header();
55
56 let is_file = header.entry_type().is_file();
57 if !is_file {
58 continue;
59 }
60
61 let mtime = header.mtime().unwrap_or(0);
62
63 let path = header.path().map_err(|e| format!("{e:?}"))?;
64 let path = path.to_string_lossy().as_ref().to_owned();
65
66 let size = header.size().map_err(|e| format!("{e:?}"))?;
67 buf.clear();
68 buf.reserve(size as usize);
69 entry.read_to_end(&mut buf).map_err(|e| format!("{e:?}"))?;
70
71 cb(path, &buf, mtime)?
72 }
73
74 Ok(())
75 }
76}
77
78#[derive(Debug)]
80pub struct JsRegistry {
81 pub context: ProxyContext,
83 pub real_resolve_fn: js_sys::Function,
85}
86
87impl PackageRegistry for JsRegistry {
88 fn resolve(&self, spec: &PackageSpec) -> Result<std::sync::Arc<Path>, PackageError> {
89 let js_spec = js_sys::Object::new();
91 js_sys::Reflect::set(&js_spec, &"name".into(), &spec.name.to_string().into()).unwrap();
92 js_sys::Reflect::set(
93 &js_spec,
94 &"namespace".into(),
95 &spec.namespace.to_string().into(),
96 )
97 .unwrap();
98 js_sys::Reflect::set(
99 &js_spec,
100 &"version".into(),
101 &spec.version.to_string().into(),
102 )
103 .unwrap();
104
105 self.real_resolve_fn
106 .call1(&self.context.clone().into(), &js_spec)
107 .map_err(|e| PackageError::Other(Some(eco_format!("{:?}", e))))
108 .and_then(|v| {
109 if v.is_undefined() {
110 Err(PackageError::NotFound(spec.clone()))
111 } else {
112 Ok(Path::new(&v.as_string().unwrap()).into())
113 }
114 })
115 }
116
117 fn packages(&self) -> &[(PackageSpec, Option<EcoString>)] {
119 &[]
120 }
121}
122
123unsafe impl Send for JsRegistry {}
127unsafe impl Sync for JsRegistry {}