diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dca6801ae..94cabd2e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -213,8 +213,13 @@ jobs: file: api/Dockerfile push: true tags: ${{ env.API_IMAGE_ID_BASE }}:${{ github.sha }} - cache-from: type=gha,scope=docker-api - cache-to: type=gha,mode=max,scope=docker-api + # Use a registry cache (not type=gha): GHA caches are branch-scoped, + # and merge_group runs execute on transient refs, so a gha cache + # written there is never readable by later runs. A registry cache is + # shared across all refs. image-manifest/oci-mediatypes are required + # for Artifact Registry to accept the cache manifest. + cache-from: type=registry,ref=${{ env.API_IMAGE_ID_BASE }}:buildcache + cache-to: type=registry,ref=${{ env.API_IMAGE_ID_BASE }}:buildcache,mode=max,image-manifest=true,oci-mediatypes=true - name: Set api_image_id output id: api_image_id diff --git a/Cargo.lock b/Cargo.lock index bdfa1fb25..5e270443a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1056,9 +1056,9 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.206.0" +version = "0.207.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2af46876eb43b3334539f548c8c106f9ad86d330ca5661c04fcdb27fd8e463" +checksum = "3be16593d05e9f94267702f8c45adf0b5d335c584e5f0637238ba73f474282f8" dependencies = [ "anyhow", "cfg-if", @@ -1110,9 +1110,9 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.110.2" +version = "0.111.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95ac3cf9d132772c24beabf61d76f6706b7d5a23cca6d3d3f4cf2d30a7ad796" +checksum = "74d39f55639b6bffe4e886d2ea33724606729d3ec9c8e05a48952a56c1cef961" dependencies = [ "async-trait", "boxed_error", diff --git a/api/Cargo.toml b/api/Cargo.toml index ea384b16f..6c488cd6c 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -95,10 +95,10 @@ flate2 = "1" thiserror = "2" async-tar = "0.4.2" async-compression = { version = "0.4", features = ["futures-io", "gzip"] } -deno_graph = "=0.110.2" +deno_graph = "=0.111.0" deno_ast = { version = "0.53.0", features = ["view"] } # sync with frontend/deno.json -deno_doc = { version = "=0.206.0", features = ["comrak"] } +deno_doc = { version = "=0.207.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 4350f0bd2..eadaf9974 100644 --- a/api/src/analysis.rs +++ b/api/src/analysis.rs @@ -164,7 +164,6 @@ async fn analyze_package_inner( skip_dynamic_deps: false, module_info_cacher: Default::default(), unstable_bytes_imports: false, - unstable_text_imports: false, jsr_metadata_store: None, unstable_css_imports: false, unstable_config_imports: false, @@ -710,7 +709,6 @@ async fn rebuild_npm_tarball_inner( skip_dynamic_deps: false, module_info_cacher: Default::default(), unstable_bytes_imports: false, - unstable_text_imports: false, jsr_metadata_store: None, unstable_css_imports: false, unstable_config_imports: false, diff --git a/api/src/api/package.rs b/api/src/api/package.rs index 11a025496..a4209841d 100644 --- a/api/src/api/package.rs +++ b/api/src/api/package.rs @@ -2624,7 +2624,6 @@ async fn analyze_deps_tree( skip_dynamic_deps: false, module_info_cacher: Default::default(), unstable_bytes_imports: false, - unstable_text_imports: false, jsr_metadata_store: None, unstable_css_imports: false, diff --git a/frontend/routes/package/(_islands)/LocalSymbolSearch.tsx b/frontend/routes/package/(_islands)/LocalSymbolSearch.tsx index 4bc869385..10ef38a4f 100644 --- a/frontend/routes/package/(_islands)/LocalSymbolSearch.tsx +++ b/frontend/routes/package/(_islands)/LocalSymbolSearch.tsx @@ -14,11 +14,7 @@ import { import { Highlight, type Position } from "@orama/highlight"; import { api, path } from "../../../utils/api.ts"; import { useMacLike } from "../../../utils/os.ts"; -import type { - AllSymbolsCtx, - AllSymbolsItemCtx, - SectionContentNamespaceSectionCtx, -} from "@deno/doc/html-types"; +import type { AllSymbolsCtx, AllSymbolsItemCtx } from "@deno/doc/html-types"; import { renderToString } from "preact-render-to-string"; import { AllSymbols } from "../../../components/doc/AllSymbols.tsx"; @@ -113,11 +109,8 @@ export function LocalSymbolSearch( for (const entrypoint of searchContent.value!.entrypoints) { for (const kindGroup of entrypoint.module_doc.sections.sections) { - for ( - const symbol - of (kindGroup.content as SectionContentNamespaceSectionCtx) - .content - ) { + if (kindGroup.content.kind !== "namespace_section") continue; + for (const symbol of kindGroup.content.content) { searchItems.push({ name: symbol.name, symbolName: symbol.name, @@ -202,32 +195,34 @@ export function LocalSymbolSearch( .map((entrypoint) => { const filteredSections = entrypoint.module_doc.sections.sections .map((kindGroup) => { - const filteredContent = - (kindGroup.content as SectionContentNamespaceSectionCtx).content - .map((symbol) => { - const symbolMatches = hitNames.has(symbol.name); - const matchingSubitems = symbol.subitems.filter((subitem) => - hitNames.has(subitem.title) - ); - - if (!symbolMatches && matchingSubitems.length === 0) { - return null; - } - - return { - ...symbol, - subitems: symbolMatches - ? symbol.subitems - : matchingSubitems, - }; - }) - .filter(Boolean); + const content = kindGroup.content; + if (content.kind !== "namespace_section") return null; + + const filteredContent = content.content + .map((symbol) => { + const symbolMatches = hitNames.has(symbol.name); + const matchingSubitems = symbol.subitems.filter((subitem) => + hitNames.has(subitem.title) + ); + + if (!symbolMatches && matchingSubitems.length === 0) { + return null; + } + + return { + ...symbol, + subitems: symbolMatches + ? symbol.subitems + : matchingSubitems, + }; + }) + .filter(Boolean); if (filteredContent.length === 0) return null; return { ...kindGroup, - content: { ...kindGroup.content, content: filteredContent }, + content: { ...content, content: filteredContent }, }; }) .filter(Boolean); diff --git a/frontend/routes/package/index.tsx b/frontend/routes/package/index.tsx index 5ffefec6b..31fa568fe 100644 --- a/frontend/routes/package/index.tsx +++ b/frontend/routes/package/index.tsx @@ -44,6 +44,19 @@ export default define.page(function PackagePage( showProvenanceBadge /> ) + : data.selectedVersion + ? ( +
+ Documentation is only available for the{" "} + + latest version + {" "} + of a package. +
+ ) : (
This package has not published{" "} diff --git a/frontend/utils/data.ts b/frontend/utils/data.ts index 8286bdca5..4ac66d74e 100644 --- a/frontend/utils/data.ts +++ b/frontend/utils/data.ts @@ -127,14 +127,35 @@ export async function packageDataWithDocs( if (pkgDocsResp.code === "scopeNotFound") return null; if (pkgDocsResp.code === "packageNotFound") return null; if (pkgDocsResp.code === "docsOnlyForLatestVersion") { - // Docs are only served for the latest version; redirect to the - // canonical (versionless) docs URL for the latest version. - return new Response(null, { - status: 302, - headers: { - Location: `/@${scope}/${pkg}/doc${compileDocsRequestPath(docs)}`, - }, - }); + if ("entrypoint" in docs || "all_symbols" in docs) { + // Docs are only served for the latest version; redirect to the + // canonical (versionless) docs URL for the latest version. + return new Response(null, { + status: 302, + headers: { + Location: `/@${scope}/${pkg}/doc${compileDocsRequestPath(docs)}`, + }, + }); + } + + // The package overview page of a non-latest version: render it + // without docs instead of redirecting away from the version. + const pkgVersionResp = await state.api.get( + path`/scopes/${scope}/packages/${pkg}/versions/${version!}`, + ); + if (!pkgVersionResp.ok) { + if (pkgVersionResp.code === "packageVersionNotFound") return null; + if (pkgVersionResp.code === "scopeNotFound") return null; + if (pkgVersionResp.code === "packageNotFound") return null; + assertOk(pkgVersionResp); + } + return { + ...data, + kind: "content", + selectedVersion: pkgVersionResp.data, + selectedVersionIsLatestUnyanked: false, + docs: null, + }; } if (pkgDocsResp.code === "entrypointOrSymbolNotFound") { // redirect to all symbols page if there is no default entrypoint