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