From 7032f82915a57a6850e24b58eaf6f7f3ca87453b Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Fri, 27 Feb 2026 16:12:12 +0100 Subject: [PATCH 1/2] feat: documentation diffing (#1319) image image image --- .github/workflows/ci.yml | 2 +- Cargo.lock | 11 +- api/Cargo.toml | 2 +- api/src/analysis.rs | 2 + api/src/api/errors.rs | 4 + api/src/api/package.rs | 211 +++++- api/src/api/types.rs | 2 +- api/src/docs.rs | 67 +- api/src/util.rs | 36 +- deno.json | 2 +- deno.lock | 8 +- frontend/components/doc/Anchor.tsx | 8 +- frontend/components/doc/DocEntry.tsx | 105 ++- frontend/components/doc/Function.tsx | 2 +- frontend/components/doc/IndexSignature.tsx | 74 ++- frontend/components/doc/NamespaceSection.tsx | 241 ++++--- frontend/components/doc/Section.tsx | 2 +- frontend/components/doc/SymbolGroup.tsx | 36 +- frontend/components/doc/Tag.tsx | 15 +- frontend/components/doc/Toc.tsx | 29 +- frontend/components/doc/mod.ts | 25 + frontend/deno.json | 2 +- frontend/deno.lock | 598 +++++++++++++++--- .../routes/package/(_components)/Docs.tsx | 269 +++++--- .../package/(_components)/PackageHeader.tsx | 70 +- .../package/(_components)/PackageNav.tsx | 17 + .../package/(_islands)/BreadcrumbsSticky.tsx | 55 +- .../(_islands)/DiffVersionSelector.tsx | 76 +++ frontend/routes/package/diff/[file].tsx | 127 ++++ frontend/routes/package/diff/[symbol].tsx | 127 ++++ frontend/routes/package/diff/all_symbols.tsx | 115 ++++ frontend/routes/package/doc/[file].tsx | 1 - frontend/routes/package/doc/[symbol].tsx | 1 - frontend/routes/package/doc/all_symbols.tsx | 1 - frontend/routes/package/index.tsx | 1 - frontend/static/styles.css | 125 +++- frontend/static/tailwind.css | 1 - frontend/util.ts | 3 +- frontend/utils/api_types.ts | 2 +- frontend/utils/data.ts | 104 ++- tools/clone_dependency.ts | 3 +- tools/dev.ts | 49 +- 42 files changed, 2172 insertions(+), 459 deletions(-) create mode 100644 frontend/routes/package/(_islands)/DiffVersionSelector.tsx create mode 100644 frontend/routes/package/diff/[file].tsx create mode 100644 frontend/routes/package/diff/[symbol].tsx create mode 100644 frontend/routes/package/diff/all_symbols.tsx mode change 100644 => 100755 tools/clone_dependency.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83bca7480..640b48f90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,7 +98,7 @@ jobs: - name: Check sqlx metadata is up to date run: | cargo sqlx migrate run - cargo sqlx prepare --check + cargo sqlx prepare --check -- --all-targets working-directory: api env: DATABASE_URL: postgres://user:password@localhost/registry diff --git a/Cargo.lock b/Cargo.lock index bf20df618..35d34031e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1187,9 +1187,9 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.193.0" +version = "0.194.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03db8870d046379f6e2aa5326d21340e7f9d937ae79e06f8bf3e86dfb6340f7" +checksum = "1264b31a19f293d6f14d3633053a1450ac3b863fa73ed5f5ba1fb10f89c07cd6" dependencies = [ "anyhow", "cfg-if", @@ -1209,6 +1209,7 @@ dependencies = [ "serde", "serde-wasm-bindgen", "serde_json", + "similar", "termcolor", "url", "wasm-bindgen", @@ -4558,6 +4559,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "simple_asn1" version = "0.6.2" diff --git a/api/Cargo.toml b/api/Cargo.toml index a3292ca4f..2da6796bf 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -83,7 +83,7 @@ async-compression = { version = "0.4", features = ["futures-io", "gzip"] } deno_graph = "=0.107.0" deno_ast = { version = "0.53.0", features = ["view"] } # sync with frontend/deno.json -deno_doc = { version = "=0.193.0", features = ["comrak"] } +deno_doc = { version = "=0.194.0", features = ["comrak"] } deno_error = "0.7.0" comrak = { version = "0.29.0", default-features = false } ammonia = "4.0.0" diff --git a/api/src/analysis.rs b/api/src/analysis.rs index 8eb790a8e..6e645eaaa 100644 --- a/api/src/analysis.rs +++ b/api/src/analysis.rs @@ -253,6 +253,7 @@ async fn analyze_package_inner( let info = crate::docs::get_docs_info(&exports, None); let ctx = crate::docs::get_generate_ctx( + "/doc".to_string(), doc_nodes, main_entrypoint, info.rewrite_map, @@ -270,6 +271,7 @@ async fn analyze_package_inner( bun: None, }, registry_url.to_string(), + None, ); let search_index = deno_doc::html::generate_search_index(&ctx); let doc_search_json = if let serde_json::Value::Object(mut obj) = search_index diff --git a/api/src/api/errors.rs b/api/src/api/errors.rs index 5dfde1d06..695322c3e 100644 --- a/api/src/api/errors.rs +++ b/api/src/api/errors.rs @@ -44,6 +44,10 @@ errors!( status: NOT_FOUND, "The requested package version was not found.", }, + DiffNoIndex { + status: NOT_FOUND, + "Diffs do not have an index.", + }, EntrypointOrSymbolNotFound { status: NOT_FOUND, "The requested entrypoint or symbol was not found.", diff --git a/api/src/api/package.rs b/api/src/api/package.rs index 257caef0b..d5bd82a4e 100644 --- a/api/src/api/package.rs +++ b/api/src/api/package.rs @@ -83,14 +83,14 @@ use crate::provenance; use crate::publish::publish_task; use crate::tarball::bucket_tarball_path; use crate::util; -use crate::util::ApiResult; -use crate::util::CacheDuration; use crate::util::LicenseStore; use crate::util::RequestIdExt; use crate::util::VersionOrLatest; use crate::util::decode_json; use crate::util::pagination; use crate::util::search; +use crate::util::{ApiResult, docs_queries}; +use crate::util::{CacheDuration, DocsQueries}; use super::ApiCreatePackageRequest; use super::ApiDependency; @@ -181,6 +181,10 @@ pub fn package_router() -> Router { "/:package/versions/:version/source", util::cache(CacheDuration::ONE_MINUTE, util::json(get_source_handler)), ) + .get( + "/:package/diff/:old_version/:new_version", + util::cache(CacheDuration::ONE_MINUTE, util::json(get_diff_handler)), + ) .get( "/:package/versions/:version/dependencies", util::json(list_dependencies_handler), @@ -1198,31 +1202,18 @@ pub async fn get_docs_handler( Span::current().record("scope", field::display(&scope)); Span::current().record("package", field::display(&package_name)); Span::current().record("version", field::display(&version_or_latest)); - let all_symbols = req.query("all_symbols").is_some(); + let DocsQueries { + all_symbols, + entrypoint, + symbol, + } = docs_queries(&req)?; + Span::current().record("all_symbols", field::display(&all_symbols)); - let entrypoint = req.query("entrypoint").map(|s| match s.as_str() { - "" => ".", - s => s, - }); Span::current() .record("entrypoint", field::display(&entrypoint.unwrap_or(""))); - - let symbol = req - .query("symbol") - .and_then(|s| match s.as_str() { - "" => None, - s => Some(urlencoding::decode(s)), - }) - .transpose()?; Span::current() .record("symbol", field::display(&symbol.as_deref().unwrap_or(""))); - if all_symbols && (entrypoint.is_some() || symbol.is_some()) { - return Err(ApiError::MalformedRequest { - msg: "Cannot specify both all_symbols and entrypoint".into(), - }); - } - let db = req.data::().unwrap(); let buckets = req.data::().unwrap(); let (package, repo, _) = db @@ -1245,6 +1236,7 @@ pub async fn get_docs_handler( let docs_path = crate::gcs_paths::docs_v1_path(&scope, &package_name, &version.version); let doc_nodes_fut = buckets.docs_bucket.download(docs_path.into()); + let readme_fut = if !all_symbols && entrypoint.is_none() && symbol.is_none() { if let Some(readme_path) = &version.readme_path { let gcs_path = crate::gcs_paths::file_path( @@ -1276,6 +1268,7 @@ pub async fn get_docs_handler( })?; let doc_nodes: DocNodesByUrl = serde_json::from_slice(&docs).context("failed to parse doc nodes")?; + let readme = readme.and_then(|readme| { std::str::from_utf8(&readme).ok().map(ToOwned::to_owned) }); @@ -1305,6 +1298,7 @@ pub async fn get_docs_handler( }; let docs = crate::docs::generate_docs_html( + "/doc".to_string(), doc_nodes, docs_info.main_entrypoint, docs_info.rewrite_map, @@ -1318,6 +1312,7 @@ pub async fn get_docs_handler( package.runtime_compat, registry_url, package.readme_source, + None, ) .map_err(|e| { error!("failed to generate docs: {}", e); @@ -1393,6 +1388,7 @@ pub async fn get_docs_search_handler( let registry_url = req.data::().unwrap().0.to_string(); let ctx = crate::docs::get_generate_ctx( + "/doc".to_string(), doc_nodes, docs_info.main_entrypoint, docs_info.rewrite_map, @@ -1404,6 +1400,7 @@ pub async fn get_docs_search_handler( false, package.runtime_compat, registry_url, + None, ); let search_index = deno_doc::html::generate_search_index(&ctx); @@ -1464,6 +1461,7 @@ pub async fn get_docs_search_structured_handler( let registry_url = req.data::().unwrap().0.to_string(); let docs = crate::docs::generate_docs_html( + "/doc".to_string(), doc_nodes, docs_info.main_entrypoint, docs_info.rewrite_map, @@ -1477,6 +1475,7 @@ pub async fn get_docs_search_structured_handler( package.runtime_compat, registry_url, package.readme_source, + None, ) .map_err(|e| { error!("failed to generate docs: {}", e); @@ -1659,6 +1658,164 @@ pub async fn get_source_handler( }) } +#[instrument( + name = "GET /api/scopes/:scope/packages/:package/diff/:old_version/:new_version", + skip(req), + err, + fields(scope, package, version, all_symbols, entrypoint, symbol) +)] +pub async fn get_diff_handler( + req: Request, +) -> ApiResult { + let scope = req.param_scope()?; + let package_name = req.param_package()?; + Span::current().record("scope", field::display(&scope)); + Span::current().record("package", field::display(&package_name)); + + let old_version = util::param(&req, "old_version")?; + let old_version = Version::try_from(old_version.as_str()).map_err(|err| { + let msg = + format!("failed to parse path parameter 'old_version': {err}").into(); + ApiError::MalformedRequest { msg } + })?; + Span::current().record("old_version", field::display(&old_version)); + + let new_version = util::param(&req, "new_version")?; + let new_version = Version::try_from(new_version.as_str()).map_err(|err| { + let msg = + format!("failed to parse path parameter 'new_version': {err}").into(); + ApiError::MalformedRequest { msg } + })?; + Span::current().record("new_version", field::display(&new_version)); + + let DocsQueries { + all_symbols, + entrypoint, + symbol, + } = docs_queries(&req)?; + if !all_symbols && entrypoint.is_none() { + return Err(ApiError::DiffNoIndex); + } + + Span::current().record("all_symbols", field::display(&all_symbols)); + Span::current() + .record("entrypoint", field::display(&entrypoint.unwrap_or(""))); + Span::current() + .record("symbol", field::display(&symbol.as_deref().unwrap_or(""))); + + let full = req.query("full").is_some(); + Span::current().record("full", field::display(full)); + + let db = req.data::().unwrap(); + let buckets = req.data::().unwrap(); + let (package, repo, _) = db + .get_package(&scope, &package_name) + .await? + .ok_or(ApiError::PackageNotFound)?; + + let old_version = db + .get_package_version(&scope, &package_name, &old_version) + .await? + .ok_or(ApiError::PackageVersionNotFound)?; + let new_version = db + .get_package_version(&scope, &package_name, &new_version) + .await? + .ok_or(ApiError::PackageVersionNotFound)?; + + let old_docs_path = + crate::gcs_paths::docs_v1_path(&scope, &package_name, &old_version.version); + let old_doc_nodes_fut = buckets.docs_bucket.download(old_docs_path.into()); + let new_docs_path = + crate::gcs_paths::docs_v1_path(&scope, &package_name, &new_version.version); + let new_doc_nodes_fut = buckets.docs_bucket.download(new_docs_path.into()); + + let (old_docs, new_docs) = + futures::future::try_join(old_doc_nodes_fut, new_doc_nodes_fut).await?; + + let old_docs = old_docs.ok_or_else(|| { + error!( + "docs not found for {}/{}/{}", + scope, package_name, old_version.version + ); + ApiError::InternalServerError + })?; + let new_docs = new_docs.ok_or_else(|| { + error!( + "docs not found for {}/{}/{}", + scope, package_name, new_version.version + ); + ApiError::InternalServerError + })?; + + let old_doc_nodes: DocNodesByUrl = + serde_json::from_slice(&old_docs).context("failed to parse doc nodes")?; + let new_doc_nodes: DocNodesByUrl = + serde_json::from_slice(&new_docs).context("failed to parse doc nodes")?; + + // diffs are applied on top of the new version + let new_docs_info = + crate::docs::get_docs_info(&new_version.exports, entrypoint); + + if entrypoint.is_some() && new_docs_info.entrypoint_url.is_none() { + return Err(ApiError::EntrypointOrSymbolNotFound); + } + + let registry_url = req.data::().unwrap().0.to_string(); + + let req = match (new_docs_info.entrypoint_url, symbol) { + _ if all_symbols => DocsRequest::AllSymbols, + (Some(entrypoint), None) => DocsRequest::File(entrypoint), + (Some(entrypoint), Some(symbol)) => { + DocsRequest::Symbol(entrypoint, symbol.into()) + } + (None, Some(symbol)) => { + if let Some(entrypoint_url) = new_docs_info.main_entrypoint.clone() { + DocsRequest::Symbol(entrypoint_url, symbol.into()) + } else { + return Err(ApiError::EntrypointOrSymbolNotFound); + } + } + (None, None) => DocsRequest::Index, + }; + + let docs = crate::docs::generate_docs_html( + format!("/diff/{}...{}", old_version.version, new_version.version), + new_doc_nodes, + new_docs_info.main_entrypoint, + new_docs_info.rewrite_map, + req, + scope.clone(), + package_name.clone(), + new_version.version.clone(), + true, + repo, + None, + package.runtime_compat, + registry_url, + package.readme_source, + Some((old_doc_nodes, full)), + ) + .map_err(|e| { + error!("failed to generate docs: {}", e); + ApiError::InternalServerError + })? + .ok_or(ApiError::EntrypointOrSymbolNotFound)?; + + match docs { + GeneratedDocsOutput::Docs(docs) => Ok(ApiPackageVersionDocs::Content { + comrak_css: Cow::Borrowed(deno_doc::html::comrak::COMRAK_STYLESHEET), + script: Cow::Borrowed(deno_doc::html::SCRIPT_JS), + breadcrumbs: docs.breadcrumbs, + toc: docs.toc, + main: docs.main.into(), + version: ApiPackageVersion::from(new_version), + }), + GeneratedDocsOutput::Redirect(href) => { + Ok(ApiPackageVersionDocs::Redirect { symbol: href }) + } + } +} + #[instrument( name = "GET /api/scopes/:scope/packages/:package/dependents", skip(req), @@ -3611,12 +3768,11 @@ ggHohNAjhbzDaY2iBW/m3NC5dehGUP4T2GBo/cwGhg== comrak_css: _, script: _, breadcrumbs, - toc, + toc: _, main: _, } => { assert_eq!(version.version, task.package_version); assert!(breadcrumbs.is_none(), "{:?}", breadcrumbs); - assert!(toc.is_some(), "{:?}", toc) } ApiPackageVersionDocs::Redirect { .. } => panic!(), } @@ -3635,12 +3791,11 @@ ggHohNAjhbzDaY2iBW/m3NC5dehGUP4T2GBo/cwGhg== comrak_css: _, script: _, breadcrumbs, - toc, + toc: _, main: _, } => { assert_eq!(version.version, task.package_version); assert!(breadcrumbs.is_some()); - assert!(toc.is_none(), "{:?}", toc); } ApiPackageVersionDocs::Redirect { .. } => panic!(), } @@ -3659,12 +3814,11 @@ ggHohNAjhbzDaY2iBW/m3NC5dehGUP4T2GBo/cwGhg== comrak_css: _, script: _, breadcrumbs, - toc, + toc: _, main: _, } => { assert_eq!(version.version, task.package_version); assert!(breadcrumbs.is_some()); - assert!(toc.is_some(), "{:?}", toc); } ApiPackageVersionDocs::Redirect { .. } => panic!(), } @@ -3686,12 +3840,11 @@ ggHohNAjhbzDaY2iBW/m3NC5dehGUP4T2GBo/cwGhg== comrak_css: _, script: _, breadcrumbs, - toc, + toc: _, main: _, } => { assert_eq!(version.version, task.package_version); assert!(breadcrumbs.is_some()); - assert!(toc.is_some(), "{:?}", toc); } ApiPackageVersionDocs::Redirect { .. } => panic!(), } diff --git a/api/src/api/types.rs b/api/src/api/types.rs index 9e4e5778a..6549c8074 100644 --- a/api/src/api/types.rs +++ b/api/src/api/types.rs @@ -636,7 +636,7 @@ pub enum ApiPackageVersionDocs { comrak_css: Cow<'static, str>, script: Cow<'static, str>, breadcrumbs: Option, - toc: Option, + toc: deno_doc::html::util::ToCCtx, main: ApiGeneratedDocsContent, }, Redirect { diff --git a/api/src/docs.rs b/api/src/docs.rs index b82725885..ba172d62a 100644 --- a/api/src/docs.rs +++ b/api/src/docs.rs @@ -308,7 +308,7 @@ pub enum GeneratedDocsOutput { #[derive(Debug)] pub struct GeneratedDocs { pub breadcrumbs: Option, - pub toc: Option, + pub toc: deno_doc::html::ToCCtx, pub main: GeneratedDocsContent, } @@ -430,10 +430,12 @@ fn get_url_rewriter( version_is_latest, has_readme, runtime_compat, - registry_url + registry_url, + diff ) )] pub fn get_generate_ctx( + doc_base: String, doc_nodes_by_url: DocNodesByUrl, main_entrypoint: Option, rewrite_map: IndexMap, @@ -445,6 +447,7 @@ pub fn get_generate_ctx( has_readme: bool, runtime_compat: RuntimeCompat, registry_url: String, + diff: Option<(deno_doc::diff::DocDiff, bool)>, ) -> GenerateCtx { let package_name = format!("@{scope}/{package}"); let url_rewriter_base = format!("/{package_name}/{version}"); @@ -506,12 +509,16 @@ pub fn get_generate_ctx( .collect() }) .clone(), + doc_base, + full: diff.as_ref().map(|diff| diff.1), + }), + usage_composer: diff.is_none().then(|| { + Rc::new(DocUsageComposer { + runtime_compat, + scope, + package, + }) as Rc }), - usage_composer: (Rc::new(DocUsageComposer { - runtime_compat, - scope, - package, - })), rewrite_map: Some(rewrite_map), category_docs: None, disable_search: false, @@ -521,10 +528,12 @@ pub fn get_generate_ctx( markdown_stripper: Rc::new(deno_doc::html::comrak::strip), head_inject: None, id_prefix: None, + diff_only: diff.as_ref().map(|diff| !diff.1).unwrap_or_default(), }, None, deno_doc::html::FileMode::Normal, doc_nodes_by_url, + diff.map(|diff| diff.0), ) .unwrap() } @@ -532,10 +541,11 @@ pub fn get_generate_ctx( #[allow(clippy::too_many_arguments)] #[instrument( name = "generate_docs_html", - skip(doc_nodes_by_url, rewrite_map, readme), + skip(doc_nodes_by_url, rewrite_map, readme, diff_data), err )] pub fn generate_docs_html( + doc_base: String, doc_nodes_by_url: DocNodesByUrl, main_entrypoint: Option, rewrite_map: IndexMap, @@ -549,8 +559,19 @@ pub fn generate_docs_html( runtime_compat: RuntimeCompat, registry_url: String, readme_source: ReadmeSource, + diff_data: Option<(DocNodesByUrl, bool)>, ) -> Result, anyhow::Error> { + let diff = if let Some((old_doc_nodes_by_url, full)) = diff_data { + Some(( + deno_doc::diff::DocDiff::diff(&old_doc_nodes_by_url, &doc_nodes_by_url), + full, + )) + } else { + None + }; + let ctx = get_generate_ctx( + doc_base, doc_nodes_by_url, main_entrypoint, rewrite_map, @@ -562,6 +583,7 @@ pub fn generate_docs_html( readme.is_some(), runtime_compat, registry_url, + diff, ); match req { @@ -572,9 +594,11 @@ pub fn generate_docs_html( let all_symbols = deno_doc::html::AllSymbolsCtx::new(&render_ctx); let breadcrumbs = render_ctx.get_breadcrumbs(); + let toc = deno_doc::html::ToCCtx::new(render_ctx, false, Some(&[])); + Ok(Some(GeneratedDocsOutput::Docs(GeneratedDocs { breadcrumbs: Some(breadcrumbs), - toc: None, + toc, main: GeneratedDocsContent::AllSymbols(all_symbols), }))) } @@ -624,11 +648,11 @@ pub fn generate_docs_html( index_module_doc.sections.docs = Some(markdown); } - let toc_ctx = deno_doc::html::ToCCtx::new(render_ctx, true, Some(&[])); + let toc = deno_doc::html::ToCCtx::new(render_ctx, true, Some(&[])); Ok(Some(GeneratedDocsOutput::Docs(GeneratedDocs { breadcrumbs: None, - toc: Some(toc_ctx), + toc, main: GeneratedDocsContent::Index(index_module_doc), }))) } @@ -657,11 +681,11 @@ pub fn generate_docs_html( let breadcrumbs = render_ctx.get_breadcrumbs(); - let toc_ctx = deno_doc::html::ToCCtx::new(render_ctx, false, Some(&[])); + let toc = deno_doc::html::ToCCtx::new(render_ctx, false, Some(&[])); Ok(Some(GeneratedDocsOutput::Docs(GeneratedDocs { breadcrumbs: Some(breadcrumbs), - toc: Some(toc_ctx), + toc, main: GeneratedDocsContent::File(module_doc), }))) } @@ -686,7 +710,7 @@ pub fn generate_docs_html( categories_panel: _categories_panel, } => Ok(Some(GeneratedDocsOutput::Docs(GeneratedDocs { breadcrumbs: Some(breadcrumbs_ctx), - toc: Some(*toc_ctx), + toc: *toc_ctx, main: GeneratedDocsContent::Symbol(symbol_group_ctx), }))), SymbolPage::Redirect { href, .. } => { @@ -740,6 +764,7 @@ fn generate_symbol_page( return Some(SymbolPage::Redirect { current_symbol: name.to_string(), href: name.rsplit_once('.').unwrap().0.to_string(), + diff_status: None, // TODO }); } else { is_static = false; @@ -992,6 +1017,8 @@ struct DocResolver { registry_url: String, deno_types: std::collections::HashSet>, web_types: std::collections::HashMap, String>, + doc_base: String, + full: Option, } impl HrefResolver for DocResolver { @@ -1010,9 +1037,9 @@ impl HrefResolver for DocResolver { String::new() } ); - let doc_base = format!("{package_base}/doc"); + let doc_base = format!("{package_base}{}", self.doc_base); - match target { + let path = match target { UrlResolveKind::Root => package_base, UrlResolveKind::AllSymbols => format!("{doc_base}/all_symbols"), UrlResolveKind::Symbol { file, symbol } => { @@ -1034,6 +1061,12 @@ impl HrefResolver for DocResolver { } ), UrlResolveKind::Category { .. } => unreachable!(), + }; + + if self.full.is_some_and(std::convert::identity) { + path + "?full" + } else { + path } } @@ -1243,6 +1276,8 @@ mod tests { registry_url: "".to_string(), deno_types: Default::default(), web_types: Default::default(), + doc_base: "/doc".to_string(), + full: None, }; let specifier = ModuleSpecifier::parse("file:///mod.ts").unwrap(); diff --git a/api/src/util.rs b/api/src/util.rs index 4995ee920..2d895eb32 100644 --- a/api/src/util.rs +++ b/api/src/util.rs @@ -299,6 +299,40 @@ pub fn pagination(req: &Request) -> (i64, i64) { (start, limit) } +pub struct DocsQueries<'a> { + pub all_symbols: bool, + pub entrypoint: Option<&'a str>, + pub symbol: Option>, +} + +pub fn docs_queries(req: &Request) -> Result, ApiError> { + let all_symbols = req.query("all_symbols").is_some(); + let entrypoint = req.query("entrypoint").map(|s| match s.as_str() { + "" => ".", + s => s, + }); + + let symbol = req + .query("symbol") + .and_then(|s| match s.as_str() { + "" => None, + s => Some(urlencoding::decode(s)), + }) + .transpose()?; + + if all_symbols && (entrypoint.is_some() || symbol.is_some()) { + return Err(ApiError::MalformedRequest { + msg: "Cannot specify both all_symbols and entrypoint".into(), + }); + } + + Ok(DocsQueries { + all_symbols, + entrypoint, + symbol, + }) +} + // Sanitize redirect urls // - Remove origin from Url: https://evil.com -> / // - Replace multiple slashes with one slash to remove prevent @@ -339,7 +373,7 @@ pub trait RequestIdExt { fn param_version_or_latest(&self) -> Result; } -fn param<'a>( +pub fn param<'a>( req: &'a Request, name: &str, ) -> Result<&'a String, ApiError> { diff --git a/deno.json b/deno.json index 8c38ee2f9..2dde101eb 100644 --- a/deno.json +++ b/deno.json @@ -42,7 +42,7 @@ "api/license-list-data" ], "imports": { - "@deno/doc": "jsr:@deno/doc@^0.193.0", + "@deno/doc": "jsr:@deno/doc@^0.194.0", "@deno/gfm": "jsr:@deno/gfm@^0.10.0", "@mdn/browser-compat-data": "npm:@mdn/browser-compat-data@^7.1.6", "@std/async": "jsr:@std/async@^1.0.8", diff --git a/deno.lock b/deno.lock index c567609dc..7f8856a73 100644 --- a/deno.lock +++ b/deno.lock @@ -2,7 +2,7 @@ "version": "5", "specifiers": { "jsr:@deno/cache-dir@0.25": "0.25.0", - "jsr:@deno/doc@0.193": "0.193.0", + "jsr:@deno/doc@0.194": "0.194.0", "jsr:@deno/gfm@0.10": "0.10.0", "jsr:@deno/graph@0.100": "0.100.1", "jsr:@deno/graph@0.86": "0.86.9", @@ -47,8 +47,8 @@ "jsr:@std/path" ] }, - "@deno/doc@0.193.0": { - "integrity": "53e86ecbbbad01caabadf8e11ec0a089b95a83d30a776c2976c279601e39cf5c", + "@deno/doc@0.194.0": { + "integrity": "eac7bc9d5061252a57b11cdfd1a24d6c773eb93004794112b23e9af937acc7eb", "dependencies": [ "jsr:@deno/cache-dir", "jsr:@deno/graph@0.100" @@ -314,7 +314,7 @@ }, "workspace": { "dependencies": [ - "jsr:@deno/doc@0.193", + "jsr:@deno/doc@0.194", "jsr:@deno/gfm@0.10", "jsr:@std/async@^1.0.8", "jsr:@std/bytes@^1.0.6", diff --git a/frontend/components/doc/Anchor.tsx b/frontend/components/doc/Anchor.tsx index 1ac47f972..9e471e63b 100644 --- a/frontend/components/doc/Anchor.tsx +++ b/frontend/components/doc/Anchor.tsx @@ -2,11 +2,15 @@ import type { AnchorCtx } from "@deno/doc/html-types"; import TbLink from "tb-icons/TbLink"; -export function Anchor({ anchor: { id } }: { anchor: AnchorCtx }) { +export function Anchor( + { anchor: { id }, class: classes }: { anchor: AnchorCtx; class?: string }, +) { return (
diff --git a/frontend/components/doc/Function.tsx b/frontend/components/doc/Function.tsx index 68e70e1df..18beec02b 100644 --- a/frontend/components/doc/Function.tsx +++ b/frontend/components/doc/Function.tsx @@ -9,7 +9,7 @@ export function Function({ func: { functions } }: { func: FunctionCtx }) {
{functions.map((func, index) => ( <> -
+
{func.name} diff --git a/frontend/components/doc/IndexSignature.tsx b/frontend/components/doc/IndexSignature.tsx index e718e86be..0ca925129 100644 --- a/frontend/components/doc/IndexSignature.tsx +++ b/frontend/components/doc/IndexSignature.tsx @@ -2,26 +2,74 @@ // deno-lint-ignore-file jsx-curly-braces import type { IndexSignatureCtx } from "@deno/doc/html-types"; import { Anchor } from "./Anchor.tsx"; +import { getDiffColor } from "./mod.ts"; export function IndexSignature( - { signature: { id, anchor, readonly, params, ts_type } }: { + { signature }: { signature: IndexSignatureCtx; }, ) { + const { + anchor, + diff_status, + old_readonly, + readonly, + old_params, + params, + old_ts_type, + ts_type, + } = signature; + + let readonlyClass; + + if (readonly == old_readonly) { + readonlyClass = ""; + } else if (readonly) { + readonlyClass = "diff-added ml-3"; + } else if (old_readonly) { + readonlyClass = "diff-removed ml-3"; + } + return ( -
+
- {readonly && {"readonly "}} - [] - + +
+ {(old_readonly ?? readonly) && ( + {"readonly "} + )} + [ + {old_params && ( + + )} + + ] + + {old_ts_type && ( + + )} + + +
); } diff --git a/frontend/components/doc/NamespaceSection.tsx b/frontend/components/doc/NamespaceSection.tsx index 77c26ac4c..052d68c67 100644 --- a/frontend/components/doc/NamespaceSection.tsx +++ b/frontend/components/doc/NamespaceSection.tsx @@ -1,124 +1,169 @@ // Copyright 2024 the JSR authors. All rights reserved. MIT license. import type { NamespaceNodeCtx } from "@deno/doc/html-types"; import { DocNodeKindIcon } from "./DocNodeKindIcon.tsx"; +import { getDiffColor } from "./mod.ts"; export function NamespaceSection({ items }: { items: NamespaceNodeCtx[] }) { return ( -
- {items.map((item) => ( -
- +
+ {items.map((item) => { + const renamedOldName = item.diff_status?.kind === "renamed" + ? item.diff_status.old_name + : undefined; + return (
-
- - {item.name} - - {item.ty && ( - <> - - {item.ty.info && ( -
- {item.ty.info} -
+
+ + +
+
+ + {renamedOldName && ( + + {renamedOldName} + + )} + + {item.name} + + + {item.ty && ( + <> + + {item.ty.info && ( +
+ {item.ty.info} +
+ )} + )} - - )} -
+
-
- {item.docs - ? ( - - ) - : ( - - No documentation available - - )} +
+ {item.docs + ? ( + + ) + : ( + + No documentation available + + )} +
+
{item.subitems && item.subitems.length > 0 && ( -
-
- ))} + ); + })}
); } diff --git a/frontend/components/doc/Section.tsx b/frontend/components/doc/Section.tsx index e871780dc..1dadb56c6 100644 --- a/frontend/components/doc/Section.tsx +++ b/frontend/components/doc/Section.tsx @@ -38,7 +38,7 @@ function SectionContent({ content }: { content: SectionContentCtx }) { ); case "doc_entry": return ( -
+
{content.content.map((entry, i) => ( - {symbols.map((symbol) => ( -
+
+ {symbols.map((symbol, i) => ( +
{symbol.kind.title_lowercase} {" "} - {name} + {renamedOldName && ( + + {renamedOldName} + + )} + + {name} +
{symbol.subtitle && (
diff --git a/frontend/components/doc/Tag.tsx b/frontend/components/doc/Tag.tsx index 665934e20..c3ab54261 100644 --- a/frontend/components/doc/Tag.tsx +++ b/frontend/components/doc/Tag.tsx @@ -1,11 +1,16 @@ // Copyright 2024 the JSR authors. All rights reserved. MIT license. -import type { Tag as TagType } from "@deno/doc/html-types"; +import type { TagCtx } from "@deno/doc/html-types"; function titleCase(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1); } -export function Tag({ tag, large }: { tag: TagType; large?: boolean }) { +export function Tag( + { tag, large }: { + tag: TagCtx; + large?: boolean; + }, +) { const sizeClasses = large ? "font-bold py-2 px-3" : "text-sm py-1 px-2"; const renderContent = () => { @@ -41,6 +46,12 @@ export function Tag({ tag, large }: { tag: TagType; large?: boolean }) {
+ {tag.diff === "added" && ( + + + )} + {tag.diff === "removed" && ( + - + )} {renderContent()}
); diff --git a/frontend/components/doc/Toc.tsx b/frontend/components/doc/Toc.tsx index 4511b7b4a..2df1725ed 100644 --- a/frontend/components/doc/Toc.tsx +++ b/frontend/components/doc/Toc.tsx @@ -6,18 +6,41 @@ import { DocNodeKindIcon } from "./DocNodeKindIcon.tsx"; import Usages from "../../islands/DocUsages.tsx"; export function Toc( - { content: { usages, top_symbols, document_navigation } }: { + { content: { usages, top_symbols, document_navigation }, diff }: { content: ToCCtx; + diff?: { + oldVersionUrl: string; + oldVersion: string; + newVersionUrl: string; + newVersion: string; + }; }, ) { - if (!usages && !top_symbols && document_navigation.length === 0) { + if (!usages && !top_symbols && document_navigation.length === 0 && !diff) { return null; } return ( -
+
+ {diff && ( + + )} + {usages && } {top_symbols && ( diff --git a/frontend/components/doc/mod.ts b/frontend/components/doc/mod.ts index 8a32c36c6..4ec2b5bd1 100644 --- a/frontend/components/doc/mod.ts +++ b/frontend/components/doc/mod.ts @@ -1,4 +1,6 @@ // Copyright 2024 the JSR authors. All rights reserved. MIT license. +import type { DiffStatus } from "@deno/doc/html-types"; + export { Anchor } from "./Anchor.tsx"; export { Breadcrumbs } from "./Breadcrumbs.tsx"; export { CategoryPanel } from "./CategoryPanel.tsx"; @@ -19,3 +21,26 @@ export { SymbolContent } from "./SymbolContent.tsx"; export { SymbolGroup } from "./SymbolGroup.tsx"; export { Tag } from "./Tag.tsx"; export { Toc } from "./Toc.tsx"; + +export function getDiffColor( + diffStatus: DiffStatus | undefined, + allowModified: boolean, +) { + if (!diffStatus) return ""; + + switch (diffStatus.kind) { + case "added": + return "diff-added"; + case "removed": + return "diff-removed"; + case "modified": { + if (allowModified) { + return "diff-modified"; + } else { + return ""; + } + } + case "renamed": + return ""; + } +} diff --git a/frontend/deno.json b/frontend/deno.json index 54758c95c..4547dab17 100644 --- a/frontend/deno.json +++ b/frontend/deno.json @@ -14,7 +14,7 @@ } }, "imports": { - "@deno/doc": "jsr:@deno/doc@^0.193.0", + "@deno/doc": "jsr:@deno/doc@^0.194.0", "@fresh/plugin-tailwind": "jsr:@fresh/plugin-tailwind@^1.0.0", "apexcharts": "npm:apexcharts@^4.5.0", "tb-icons": "jsr:@preact-icons/tb@^1.0.14", diff --git a/frontend/deno.lock b/frontend/deno.lock index b6c37386e..18699c747 100644 --- a/frontend/deno.lock +++ b/frontend/deno.lock @@ -1,13 +1,17 @@ { "version": "5", "specifiers": { + "jsr:@david/console-static-text@0.3": "0.3.0", + "jsr:@david/dax@0.43.2": "0.43.2", + "jsr:@david/path@0.2": "0.2.0", + "jsr:@david/which@~0.4.1": "0.4.1", "jsr:@deno/cache-dir@0.25": "0.25.0", - "jsr:@deno/doc@0.193": "0.193.0", - "jsr:@deno/esbuild-plugin@^1.2.0": "1.2.1", + "jsr:@deno/doc@0.194": "0.194.0", + "jsr:@deno/esbuild-plugin@^1.2.0": "1.2.0", "jsr:@deno/gfm@0.11": "0.11.0", "jsr:@deno/graph@0.100": "0.100.1", "jsr:@deno/graph@0.86": "0.86.9", - "jsr:@deno/loader@~0.3.10": "0.3.12", + "jsr:@deno/loader@~0.3.3": "0.3.12", "jsr:@denosaurs/emoji@~0.3.1": "0.3.1", "jsr:@fresh/build-id@1": "1.0.1", "jsr:@fresh/core@2": "2.2.0", @@ -15,18 +19,21 @@ "jsr:@fresh/plugin-tailwind@1": "1.0.0", "jsr:@preact-icons/common@^1.1.0": "1.1.0", "jsr:@preact-icons/tb@^1.0.14": "1.0.14", + "jsr:@std/assert@1": "1.0.18", "jsr:@std/bytes@^1.0.6": "1.0.6", - "jsr:@std/cli@^1.0.27": "1.0.27", + "jsr:@std/cli@^1.0.27": "1.0.28", "jsr:@std/collections@^1.1.3": "1.1.5", "jsr:@std/encoding@^1.0.10": "1.0.10", + "jsr:@std/fmt@1": "1.0.9", "jsr:@std/fmt@^1.0.3": "1.0.9", "jsr:@std/fmt@^1.0.6": "1.0.9", "jsr:@std/fmt@^1.0.8": "1.0.9", "jsr:@std/fmt@^1.0.9": "1.0.9", "jsr:@std/front-matter@1": "1.0.9", - "jsr:@std/fs@^1.0.19": "1.0.22", - "jsr:@std/fs@^1.0.22": "1.0.22", - "jsr:@std/fs@^1.0.6": "1.0.22", + "jsr:@std/fs@1": "1.0.23", + "jsr:@std/fs@^1.0.19": "1.0.23", + "jsr:@std/fs@^1.0.22": "1.0.23", + "jsr:@std/fs@^1.0.6": "1.0.23", "jsr:@std/html@^1.0.5": "1.0.5", "jsr:@std/http@1": "1.0.24", "jsr:@std/http@^1.0.21": "1.0.24", @@ -45,7 +52,7 @@ "jsr:@std/semver@^1.0.6": "1.0.8", "jsr:@std/streams@^1.0.17": "1.0.17", "jsr:@std/toml@^1.0.3": "1.0.11", - "jsr:@std/uuid@^1.0.9": "1.1.0", + "jsr:@std/uuid@^1.0.9": "1.0.9", "jsr:@std/yaml@^1.0.5": "1.0.11", "npm:@opentelemetry/api@^1.9.0": "1.9.0", "npm:@orama/core@^1.2.17": "1.2.17", @@ -53,15 +60,17 @@ "npm:@orama/orama@2": "2.1.1", "npm:@preact/signals@2.7.0": "2.7.0_preact@10.28.3", "npm:@preact/signals@^2.2.1": "2.7.0_preact@10.28.3", - "npm:@tailwindcss/postcss@^4.1.10": "4.1.18", + "npm:@tailwindcss/postcss@^4.1.10": "4.2.0", "npm:@viz-js/viz@^3.11.0": "3.24.0", "npm:apexcharts@^4.5.0": "4.7.0_@svgdotjs+svg.js@3.2.5_@svgdotjs+svg.select.js@4.0.3__@svgdotjs+svg.js@3.2.5", + "npm:browserslist@4.23.0": "4.23.0", "npm:esbuild-wasm@~0.25.11": "0.25.12", "npm:esbuild@0.25.7": "0.25.7", "npm:esbuild@~0.25.5": "0.25.7", "npm:github-slugger@2": "2.0.0", "npm:he@^1.2.0": "1.2.0", "npm:katex@0.16": "0.16.28", + "npm:lightningcss@^1.26.0": "1.31.1", "npm:marked-alert@2": "2.1.2_marked@12.0.2", "npm:marked-footnote@^1.2.0": "1.4.0_marked@12.0.2", "npm:marked-gfm-heading-id@^3.1.0": "3.2.0_marked@12.0.2", @@ -70,17 +79,43 @@ "npm:postcss@8.4": "8.4.49", "npm:postcss@8.5.6": "8.5.6", "npm:preact-render-to-string@6.3.1": "6.3.1_preact@10.28.3", - "npm:preact-render-to-string@^6.6.3": "6.6.5_preact@10.28.3", + "npm:preact-render-to-string@^6.6.3": "6.6.6_preact@10.28.3", "npm:preact@10": "10.28.3", "npm:preact@^10.22.1": "10.28.3", "npm:preact@^10.27.0": "10.28.3", "npm:preact@^10.27.2": "10.28.3", "npm:prismjs@^1.29.0": "1.30.0", "npm:sanitize-html@^2.13.0": "2.17.0", + "npm:tailwindcss@3.4.3": "3.4.3_postcss@8.5.6", "npm:tailwindcss@4.2": "4.2.0", "npm:twas@^2.1.3": "2.1.3" }, "jsr": { + "@david/console-static-text@0.3.0": { + "integrity": "2dfb46ecee525755f7989f94ece30bba85bd8ffe3e8666abc1bf926e1ee0698d" + }, + "@david/dax@0.43.2": { + "integrity": "8c7df175465d994c0e3568e3eb91102768c2f1c86d2a513d7fc4cab13f9cb328", + "dependencies": [ + "jsr:@david/console-static-text", + "jsr:@david/path", + "jsr:@david/which", + "jsr:@std/fmt@1", + "jsr:@std/fs@1", + "jsr:@std/io", + "jsr:@std/path@1" + ] + }, + "@david/path@0.2.0": { + "integrity": "f2d7aa7f02ce5a55e27c09f9f1381794acb09d328f8d3c8a2e3ab3ffc294dccd", + "dependencies": [ + "jsr:@std/fs@1", + "jsr:@std/path@1" + ] + }, + "@david/which@0.4.1": { + "integrity": "896a682b111f92ab866cc70c5b4afab2f5899d2f9bde31ed00203b9c250f225e" + }, "@deno/cache-dir@0.25.0": { "integrity": "e9c3e901f87961a813a197b076668007699953dcbeb3af51971d90fdb0d29723", "dependencies": [ @@ -91,15 +126,15 @@ "jsr:@std/path@^1.0.8" ] }, - "@deno/doc@0.193.0": { - "integrity": "53e86ecbbbad01caabadf8e11ec0a089b95a83d30a776c2976c279601e39cf5c", + "@deno/doc@0.194.0": { + "integrity": "eac7bc9d5061252a57b11cdfd1a24d6c773eb93004794112b23e9af937acc7eb", "dependencies": [ "jsr:@deno/cache-dir", "jsr:@deno/graph@0.100" ] }, - "@deno/esbuild-plugin@1.2.1": { - "integrity": "df629467913adc1f960149fdfa3a3430ba8c20381c310fba096db244e6c3c9f6", + "@deno/esbuild-plugin@1.2.0": { + "integrity": "04ddd0fca9416d8a2866263928a53b9d5ed08dfca064d64504a0aaf9800c709e", "dependencies": [ "jsr:@deno/loader", "jsr:@std/path@^1.1.1", @@ -184,11 +219,17 @@ "npm:preact@^10.22.1" ] }, + "@std/assert@1.0.18": { + "integrity": "270245e9c2c13b446286de475131dc688ca9abcd94fc5db41d43a219b34d1c78", + "dependencies": [ + "jsr:@std/internal" + ] + }, "@std/bytes@1.0.6": { "integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a" }, - "@std/cli@1.0.27": { - "integrity": "eba97edd0891871a7410e835dd94b3c260c709cca5983df2689c25a71fbe04de" + "@std/cli@1.0.28": { + "integrity": "74ef9b976db59ca6b23a5283469c9072be6276853807a83ec6c7ce412135c70a" }, "@std/collections@1.1.5": { "integrity": "c37cc6bbbbf90fec3e782535a46c9c8a3210f19118c61add71fcea86b0ffd491" @@ -206,9 +247,10 @@ "jsr:@std/yaml" ] }, - "@std/fs@1.0.22": { - "integrity": "de0f277a58a867147a8a01bc1b181d0dfa80bfddba8c9cf2bacd6747bcec9308", + "@std/fs@1.0.23": { + "integrity": "3ecbae4ce4fee03b180fa710caff36bb5adb66631c46a6460aaad49515565a37", "dependencies": [ + "jsr:@std/internal", "jsr:@std/path@^1.1.4" ] }, @@ -271,8 +313,8 @@ "jsr:@std/collections" ] }, - "@std/uuid@1.1.0": { - "integrity": "6268db2ccf172849c9be80763354ca305d49ef4af41fe995623d44fcc3f7457c", + "@std/uuid@1.0.9": { + "integrity": "44b627bf2d372fe1bd099e2ad41b2be41a777fc94e62a3151006895a037f1642", "dependencies": [ "jsr:@std/bytes" ] @@ -445,6 +487,23 @@ "@noble/hashes@1.8.0": { "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==" }, + "@nodelib/fs.scandir@2.1.5": { + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": [ + "@nodelib/fs.stat", + "run-parallel" + ] + }, + "@nodelib/fs.stat@2.0.5": { + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk@1.2.8": { + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": [ + "@nodelib/fs.scandir", + "fastq" + ] + }, "@opentelemetry/api@1.9.0": { "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==" }, @@ -508,79 +567,79 @@ "@svgdotjs/svg.js" ] }, - "@tailwindcss/node@4.1.18": { - "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", + "@tailwindcss/node@4.2.0": { + "integrity": "sha512-Yv+fn/o2OmL5fh/Ir62VXItdShnUxfpkMA4Y7jdeC8O81WPB8Kf6TT6GSHvnqgSwDzlB5iT7kDpeXxLsUS0T6Q==", "dependencies": [ "@jridgewell/remapping", "enhanced-resolve", - "jiti", + "jiti@2.6.1", "lightningcss", "magic-string", "source-map-js", - "tailwindcss@4.1.18" + "tailwindcss@4.2.0" ] }, - "@tailwindcss/oxide-android-arm64@4.1.18": { - "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", + "@tailwindcss/oxide-android-arm64@4.2.0": { + "integrity": "sha512-F0QkHAVaW/JNBWl4CEKWdZ9PMb0khw5DCELAOnu+RtjAfx5Zgw+gqCHFvqg3AirU1IAd181fwOtJQ5I8Yx5wtw==", "os": ["android"], "cpu": ["arm64"] }, - "@tailwindcss/oxide-darwin-arm64@4.1.18": { - "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", + "@tailwindcss/oxide-darwin-arm64@4.2.0": { + "integrity": "sha512-I0QylkXsBsJMZ4nkUNSR04p6+UptjcwhcVo3Zu828ikiEqHjVmQL9RuQ6uT/cVIiKpvtVA25msu/eRV97JeNSA==", "os": ["darwin"], "cpu": ["arm64"] }, - "@tailwindcss/oxide-darwin-x64@4.1.18": { - "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", + "@tailwindcss/oxide-darwin-x64@4.2.0": { + "integrity": "sha512-6TmQIn4p09PBrmnkvbYQ0wbZhLtbaksCDx7Y7R3FYYx0yxNA7xg5KP7dowmQ3d2JVdabIHvs3Hx4K3d5uCf8xg==", "os": ["darwin"], "cpu": ["x64"] }, - "@tailwindcss/oxide-freebsd-x64@4.1.18": { - "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", + "@tailwindcss/oxide-freebsd-x64@4.2.0": { + "integrity": "sha512-qBudxDvAa2QwGlq9y7VIzhTvp2mLJ6nD/G8/tI70DCDoneaUeLWBJaPcbfzqRIWraj+o969aDQKvKW9dvkUizw==", "os": ["freebsd"], "cpu": ["x64"] }, - "@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18": { - "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", + "@tailwindcss/oxide-linux-arm-gnueabihf@4.2.0": { + "integrity": "sha512-7XKkitpy5NIjFZNUQPeUyNJNJn1CJeV7rmMR+exHfTuOsg8rxIO9eNV5TSEnqRcaOK77zQpsyUkBWmPy8FgdSg==", "os": ["linux"], "cpu": ["arm"] }, - "@tailwindcss/oxide-linux-arm64-gnu@4.1.18": { - "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", + "@tailwindcss/oxide-linux-arm64-gnu@4.2.0": { + "integrity": "sha512-Mff5a5Q3WoQR01pGU1gr29hHM1N93xYrKkGXfPw/aRtK4bOc331Ho4Tgfsm5WDGvpevqMpdlkCojT3qlCQbCpA==", "os": ["linux"], "cpu": ["arm64"] }, - "@tailwindcss/oxide-linux-arm64-musl@4.1.18": { - "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", + "@tailwindcss/oxide-linux-arm64-musl@4.2.0": { + "integrity": "sha512-XKcSStleEVnbH6W/9DHzZv1YhjE4eSS6zOu2eRtYAIh7aV4o3vIBs+t/B15xlqoxt6ef/0uiqJVB6hkHjWD/0A==", "os": ["linux"], "cpu": ["arm64"] }, - "@tailwindcss/oxide-linux-x64-gnu@4.1.18": { - "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", + "@tailwindcss/oxide-linux-x64-gnu@4.2.0": { + "integrity": "sha512-/hlXCBqn9K6fi7eAM0RsobHwJYa5V/xzWspVTzxnX+Ft9v6n+30Pz8+RxCn7sQL/vRHHLS30iQPrHQunu6/vJA==", "os": ["linux"], "cpu": ["x64"] }, - "@tailwindcss/oxide-linux-x64-musl@4.1.18": { - "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", + "@tailwindcss/oxide-linux-x64-musl@4.2.0": { + "integrity": "sha512-lKUaygq4G7sWkhQbfdRRBkaq4LY39IriqBQ+Gk6l5nKq6Ay2M2ZZb1tlIyRNgZKS8cbErTwuYSor0IIULC0SHw==", "os": ["linux"], "cpu": ["x64"] }, - "@tailwindcss/oxide-wasm32-wasi@4.1.18": { - "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", + "@tailwindcss/oxide-wasm32-wasi@4.2.0": { + "integrity": "sha512-xuDjhAsFdUuFP5W9Ze4k/o4AskUtI8bcAGU4puTYprr89QaYFmhYOPfP+d1pH+k9ets6RoE23BXZM1X1jJqoyw==", "cpu": ["wasm32"] }, - "@tailwindcss/oxide-win32-arm64-msvc@4.1.18": { - "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", + "@tailwindcss/oxide-win32-arm64-msvc@4.2.0": { + "integrity": "sha512-2UU/15y1sWDEDNJXxEIrfWKC2Yb4YgIW5Xz2fKFqGzFWfoMHWFlfa1EJlGO2Xzjkq/tvSarh9ZTjvbxqWvLLXA==", "os": ["win32"], "cpu": ["arm64"] }, - "@tailwindcss/oxide-win32-x64-msvc@4.1.18": { - "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", + "@tailwindcss/oxide-win32-x64-msvc@4.2.0": { + "integrity": "sha512-CrFadmFoc+z76EV6LPG1jx6XceDsaCG3lFhyLNo/bV9ByPrE+FnBPckXQVP4XRkN76h3Fjt/a+5Er/oA/nCBvQ==", "os": ["win32"], "cpu": ["x64"] }, - "@tailwindcss/oxide@4.1.18": { - "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", + "@tailwindcss/oxide@4.2.0": { + "integrity": "sha512-AZqQzADaj742oqn2xjl5JbIOzZB/DGCYF/7bpvhA8KvjUj9HJkag6bBuwZvH1ps6dfgxNHyuJVlzSr2VpMgdTQ==", "optionalDependencies": [ "@tailwindcss/oxide-android-arm64", "@tailwindcss/oxide-darwin-arm64", @@ -596,14 +655,14 @@ "@tailwindcss/oxide-win32-x64-msvc" ] }, - "@tailwindcss/postcss@4.1.18": { - "integrity": "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==", + "@tailwindcss/postcss@4.2.0": { + "integrity": "sha512-u6YBacGpOm/ixPfKqfgrJEjMfrYmPD7gEFRoygS/hnQaRtV0VCBdpkx5Ouw9pnaLRwwlgGCuJw8xLpaR0hOrQg==", "dependencies": [ "@alloc/quick-lru", "@tailwindcss/node", "@tailwindcss/oxide", "postcss@8.5.6", - "tailwindcss@4.1.18" + "tailwindcss@4.2.0" ] }, "@viz-js/viz@3.24.0": { @@ -612,6 +671,16 @@ "@yr/monotone-cubic-spline@1.0.3": { "integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==" }, + "any-promise@1.3.0": { + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "anymatch@3.1.3": { + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": [ + "normalize-path", + "picomatch@2.3.1" + ] + }, "apexcharts@4.7.0_@svgdotjs+svg.js@3.2.5_@svgdotjs+svg.select.js@4.0.3__@svgdotjs+svg.js@3.2.5": { "integrity": "sha512-iZSrrBGvVlL+nt2B1NpqfDuBZ9jX61X9I2+XV0hlYXHtTwhwLTHDKGXjNXAgFBDLuvSYCB/rq2nPWVPRv2DrGA==", "dependencies": [ @@ -623,15 +692,71 @@ "@yr/monotone-cubic-spline" ] }, + "arg@5.0.2": { + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "binary-extensions@2.3.0": { + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" + }, + "braces@3.0.3": { + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": [ + "fill-range" + ] + }, + "browserslist@4.23.0": { + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dependencies": [ + "caniuse-lite", + "electron-to-chromium", + "node-releases", + "update-browserslist-db" + ], + "bin": true + }, + "camelcase-css@2.0.1": { + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, + "caniuse-lite@1.0.30001770": { + "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==" + }, + "chokidar@3.6.0": { + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": [ + "anymatch", + "braces", + "glob-parent@5.1.2", + "is-binary-path", + "is-glob", + "normalize-path", + "readdirp" + ], + "optionalDependencies": [ + "fsevents" + ] + }, + "commander@4.1.1": { + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + }, "commander@8.3.0": { "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" }, + "cssesc@3.0.0": { + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": true + }, "deepmerge@4.3.1": { "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" }, "detect-libc@2.1.2": { "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" }, + "didyoumean@1.2.2": { + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "dlv@1.1.3": { + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, "dom-serializer@2.0.0": { "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dependencies": [ @@ -657,6 +782,9 @@ "domhandler" ] }, + "electron-to-chromium@1.5.286": { + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==" + }, "enhanced-resolve@5.19.0": { "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dependencies": [ @@ -704,15 +832,75 @@ "scripts": true, "bin": true }, + "escalade@3.2.0": { + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" + }, "escape-string-regexp@4.0.0": { "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, + "fast-glob@3.3.3": { + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dependencies": [ + "@nodelib/fs.stat", + "@nodelib/fs.walk", + "glob-parent@5.1.2", + "merge2", + "micromatch" + ] + }, + "fastq@1.20.1": { + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dependencies": [ + "reusify" + ] + }, + "fdir@6.5.0_picomatch@4.0.3": { + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dependencies": [ + "picomatch@4.0.3" + ], + "optionalPeers": [ + "picomatch@4.0.3" + ] + }, + "fill-range@7.1.1": { + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": [ + "to-regex-range" + ] + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "os": ["darwin"], + "scripts": true + }, + "function-bind@1.1.2": { + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, "github-slugger@2.0.0": { "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" }, + "glob-parent@5.1.2": { + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": [ + "is-glob" + ] + }, + "glob-parent@6.0.2": { + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": [ + "is-glob" + ] + }, "graceful-fs@4.2.11": { "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, + "hasown@2.0.2": { + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": [ + "function-bind" + ] + }, "he@1.2.0": { "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "bin": true @@ -726,9 +914,37 @@ "entities" ] }, + "is-binary-path@2.1.0": { + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": [ + "binary-extensions" + ] + }, + "is-core-module@2.16.1": { + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": [ + "hasown" + ] + }, + "is-extglob@2.1.1": { + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob@4.0.3": { + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": [ + "is-extglob" + ] + }, + "is-number@7.0.0": { + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, "is-plain-object@5.0.0": { "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" }, + "jiti@1.21.7": { + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "bin": true + }, "jiti@2.6.1": { "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "bin": true @@ -736,67 +952,67 @@ "katex@0.16.28": { "integrity": "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg==", "dependencies": [ - "commander" + "commander@8.3.0" ], "bin": true }, - "lightningcss-android-arm64@1.30.2": { - "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "lightningcss-android-arm64@1.31.1": { + "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", "os": ["android"], "cpu": ["arm64"] }, - "lightningcss-darwin-arm64@1.30.2": { - "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "lightningcss-darwin-arm64@1.31.1": { + "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", "os": ["darwin"], "cpu": ["arm64"] }, - "lightningcss-darwin-x64@1.30.2": { - "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "lightningcss-darwin-x64@1.31.1": { + "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", "os": ["darwin"], "cpu": ["x64"] }, - "lightningcss-freebsd-x64@1.30.2": { - "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "lightningcss-freebsd-x64@1.31.1": { + "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", "os": ["freebsd"], "cpu": ["x64"] }, - "lightningcss-linux-arm-gnueabihf@1.30.2": { - "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "lightningcss-linux-arm-gnueabihf@1.31.1": { + "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", "os": ["linux"], "cpu": ["arm"] }, - "lightningcss-linux-arm64-gnu@1.30.2": { - "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "lightningcss-linux-arm64-gnu@1.31.1": { + "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", "os": ["linux"], "cpu": ["arm64"] }, - "lightningcss-linux-arm64-musl@1.30.2": { - "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "lightningcss-linux-arm64-musl@1.31.1": { + "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", "os": ["linux"], "cpu": ["arm64"] }, - "lightningcss-linux-x64-gnu@1.30.2": { - "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "lightningcss-linux-x64-gnu@1.31.1": { + "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", "os": ["linux"], "cpu": ["x64"] }, - "lightningcss-linux-x64-musl@1.30.2": { - "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "lightningcss-linux-x64-musl@1.31.1": { + "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", "os": ["linux"], "cpu": ["x64"] }, - "lightningcss-win32-arm64-msvc@1.30.2": { - "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "lightningcss-win32-arm64-msvc@1.31.1": { + "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", "os": ["win32"], "cpu": ["arm64"] }, - "lightningcss-win32-x64-msvc@1.30.2": { - "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "lightningcss-win32-x64-msvc@1.31.1": { + "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", "os": ["win32"], "cpu": ["x64"] }, - "lightningcss@1.30.2": { - "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "lightningcss@1.31.1": { + "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", "dependencies": [ "detect-libc" ], @@ -814,6 +1030,15 @@ "lightningcss-win32-x64-msvc" ] }, + "lilconfig@2.1.0": { + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" + }, + "lilconfig@3.1.3": { + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==" + }, + "lines-and-columns@1.2.4": { + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, "magic-string@0.30.21": { "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dependencies": [ @@ -850,16 +1075,105 @@ "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", "bin": true }, + "merge2@1.4.1": { + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "micromatch@4.0.8": { + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": [ + "braces", + "picomatch@2.3.1" + ] + }, + "mz@2.7.0": { + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": [ + "any-promise", + "object-assign", + "thenify-all" + ] + }, "nanoid@3.3.11": { "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "bin": true }, + "node-releases@2.0.27": { + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==" + }, + "normalize-path@3.0.0": { + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "object-assign@4.1.1": { + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-hash@3.0.0": { + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, "parse-srcset@1.0.2": { "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" }, + "path-parse@1.0.7": { + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, "picocolors@1.1.1": { "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, + "picomatch@2.3.1": { + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "picomatch@4.0.3": { + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==" + }, + "pify@2.3.0": { + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pirates@4.0.7": { + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==" + }, + "postcss-import@15.1.0_postcss@8.5.6": { + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dependencies": [ + "postcss@8.5.6", + "postcss-value-parser", + "read-cache", + "resolve" + ] + }, + "postcss-js@4.1.0_postcss@8.5.6": { + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dependencies": [ + "camelcase-css", + "postcss@8.5.6" + ] + }, + "postcss-load-config@4.0.2_postcss@8.5.6": { + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dependencies": [ + "lilconfig@3.1.3", + "postcss@8.5.6", + "yaml" + ], + "optionalPeers": [ + "postcss@8.5.6" + ] + }, + "postcss-nested@6.2.0_postcss@8.5.6": { + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dependencies": [ + "postcss@8.5.6", + "postcss-selector-parser" + ] + }, + "postcss-selector-parser@6.1.2": { + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dependencies": [ + "cssesc", + "util-deprecate" + ] + }, + "postcss-value-parser@4.2.0": { + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "postcss@8.4.49": { "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dependencies": [ @@ -883,8 +1197,8 @@ "pretty-format" ] }, - "preact-render-to-string@6.6.5_preact@10.28.3": { - "integrity": "sha512-O6MHzYNIKYaiSX3bOw0gGZfEbOmlIDtDfWwN1JJdc/T3ihzRT6tGGSEWE088dWrEDGa1u7101q+6fzQnO9XCPA==", + "preact-render-to-string@6.6.6_preact@10.28.3": { + "integrity": "sha512-EfqZJytnjJldV+YaaqhthU2oXsEf5e+6rDv957p+zxAvNfFLQOPfvBOTncscQ+akzu6Wrl7s3Pa0LjUQmWJsGQ==", "dependencies": [ "preact" ] @@ -898,6 +1212,39 @@ "prismjs@1.30.0": { "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==" }, + "queue-microtask@1.2.3": { + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "read-cache@1.0.0": { + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": [ + "pify" + ] + }, + "readdirp@3.6.0": { + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": [ + "picomatch@2.3.1" + ] + }, + "resolve@1.22.11": { + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dependencies": [ + "is-core-module", + "path-parse", + "supports-preserve-symlinks-flag" + ], + "bin": true + }, + "reusify@1.1.0": { + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" + }, + "run-parallel@1.2.0": { + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dependencies": [ + "queue-microtask" + ] + }, "sanitize-html@2.17.0": { "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", "dependencies": [ @@ -916,8 +1263,49 @@ "source-map-js@1.2.1": { "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" }, - "tailwindcss@4.1.18": { - "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==" + "sucrase@3.35.1": { + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dependencies": [ + "@jridgewell/gen-mapping", + "commander@4.1.1", + "lines-and-columns", + "mz", + "pirates", + "tinyglobby", + "ts-interface-checker" + ], + "bin": true + }, + "supports-preserve-symlinks-flag@1.0.0": { + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "tailwindcss@3.4.3_postcss@8.5.6": { + "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", + "dependencies": [ + "@alloc/quick-lru", + "arg", + "chokidar", + "didyoumean", + "dlv", + "fast-glob", + "glob-parent@6.0.2", + "is-glob", + "jiti@1.21.7", + "lilconfig@2.1.0", + "micromatch", + "normalize-path", + "object-hash", + "picocolors", + "postcss@8.5.6", + "postcss-import", + "postcss-js", + "postcss-load-config", + "postcss-nested", + "postcss-selector-parser", + "resolve", + "sucrase" + ], + "bin": true }, "tailwindcss@4.2.0": { "integrity": "sha512-yYzTZ4++b7fNYxFfpnberEEKu43w44aqDMNM9MHMmcKuCH7lL8jJ4yJ7LGHv7rSwiqM0nkiobF9I6cLlpS2P7Q==" @@ -925,8 +1313,52 @@ "tapable@2.3.0": { "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==" }, + "thenify-all@1.6.0": { + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": [ + "thenify" + ] + }, + "thenify@3.3.1": { + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": [ + "any-promise" + ] + }, + "tinyglobby@0.2.15_picomatch@4.0.3": { + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dependencies": [ + "fdir", + "picomatch@4.0.3" + ] + }, + "to-regex-range@5.0.1": { + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": [ + "is-number" + ] + }, + "ts-interface-checker@0.1.13": { + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, "twas@2.1.3": { "integrity": "sha512-4Spnweu5OEBG9ZZIfabEh0js2x1p+34QsLLz+vHjER/nQX0L/+b7H6gelZbT+Ewi2KVNHcBy5gGhib9DeVAqpA==" + }, + "update-browserslist-db@1.2.3_browserslist@4.23.0": { + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dependencies": [ + "browserslist", + "escalade", + "picocolors" + ], + "bin": true + }, + "util-deprecate@1.0.2": { + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "yaml@2.8.2": { + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "bin": true } }, "remote": { @@ -1042,7 +1474,7 @@ }, "workspace": { "dependencies": [ - "jsr:@deno/doc@0.193", + "jsr:@deno/doc@0.194", "jsr:@deno/gfm@0.11", "jsr:@fresh/core@^2.2.0", "jsr:@fresh/plugin-tailwind@1", diff --git a/frontend/routes/package/(_components)/Docs.tsx b/frontend/routes/package/(_components)/Docs.tsx index 008bf2dda..e302a94b2 100644 --- a/frontend/routes/package/(_components)/Docs.tsx +++ b/frontend/routes/package/(_components)/Docs.tsx @@ -6,16 +6,17 @@ import type { } from "../../../utils/api_types.ts"; import { LocalSymbolSearch } from "../(_islands)/LocalSymbolSearch.tsx"; import { Docs } from "../../../util.ts"; -import { Params } from "./PackageNav.tsx"; import { BreadcrumbsSticky } from "../(_islands)/BreadcrumbsSticky.tsx"; import { TicketModal } from "../../../islands/TicketModal.tsx"; import { TbFlag } from "tb-icons"; import { ModuleDoc, SymbolGroup, Toc } from "../../../components/doc/mod.ts"; import { AllSymbols } from "../../../components/doc/AllSymbols.tsx"; +import { ComponentChildren } from "preact"; +import DiffVersionSelector from "../(_islands)/DiffVersionSelector.tsx"; +import { compileDocsRequestPath, DocsRequest } from "../../../utils/data.ts"; interface DocsProps { docs: Docs; - params: Params; selectedVersion: PackageVersionWithUser; showProvenanceBadge?: boolean; user: User | null; @@ -23,6 +24,17 @@ interface DocsProps { pkg: string; } +interface DiffProps { + docs: Docs | null; + versions: PackageVersionWithUser[]; + scope: string; + pkg: string; + oldVersion?: string; + newVersion?: string; + url: URL; + request: DocsRequest; +} + interface ProvenanceBadgeProps { rekorLogId: string; } @@ -71,17 +83,21 @@ function ProvenanceBadge({ rekorLogId }: ProvenanceBadgeProps) { ); } -export function DocsView({ +function SharedView({ docs, - params, - selectedVersion, - showProvenanceBadge, - user, - scope, - pkg, -}: DocsProps) { + navRightClass, + navRight, + children, + toc, +}: { + docs: Docs; + navRightClass?: string; + navRight: ComponentChildren; + children?: ComponentChildren; + toc: ComponentChildren; +}) { return ( -
+