feat(website): escape url parts

This commit is contained in:
daimond113 2025-01-10 14:07:52 +01:00
parent dcc869c025
commit a39b1bb60a
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
17 changed files with 32 additions and 30 deletions

View file

@ -23,7 +23,7 @@ the following content:
api = "https://registry.acme.local/" api = "https://registry.acme.local/"
# package download URL (optional) # package download URL (optional)
download = "{API_URL}/v0/packages/{PACKAGE}/{PACKAGE_VERSION}/{PACKAGE_TARGET}" download = "{API_URL}/v0/packages/{PACKAGE}/{PACKAGE_VERSION}/{PACKAGE_TARGET}/archive"
# the client ID of the GitHub OAuth app (optional) # the client ID of the GitHub OAuth app (optional)
github_oauth_client_id = "a1d648966fdfbdcd9295" github_oauth_client_id = "a1d648966fdfbdcd9295"
@ -58,7 +58,7 @@ scripts_packages = ["pesde/scripts_rojo"]
- `{PACKAGE_VERSION}`: The package version. - `{PACKAGE_VERSION}`: The package version.
- `{PACKAGE_TARGET}`: The package target. - `{PACKAGE_TARGET}`: The package target.
Defaults to `{API_URL}/v0/packages/{PACKAGE}/{PACKAGE_VERSION}/{PACKAGE_TARGET}`. Defaults to `{API_URL}/v0/packages/{PACKAGE}/{PACKAGE_VERSION}/{PACKAGE_TARGET}/archive`.
- **github_oauth_client_id**: This is required if you use GitHub OAuth for - **github_oauth_client_id**: This is required if you use GitHub OAuth for
authentication. See below for more information. authentication. See below for more information.

View file

@ -1,6 +1,6 @@
use gix::Url; use gix::Url;
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use reqwest::header::{ACCEPT, AUTHORIZATION}; use reqwest::header::AUTHORIZATION;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::{BTreeMap, BTreeSet, HashSet}, collections::{BTreeMap, BTreeSet, HashSet},
@ -220,7 +220,7 @@ impl PackageSource for PesdePackageSource {
&urlencoding::encode(&id.version_id().target().to_string()), &urlencoding::encode(&id.version_id().target().to_string()),
); );
let mut request = reqwest.get(&url).header(ACCEPT, "application/octet-stream"); let mut request = reqwest.get(&url);
if let Some(token) = project.auth_config().tokens().get(&self.repo_url) { if let Some(token) = project.auth_config().tokens().get(&self.repo_url) {
tracing::debug!("using token for {}", self.repo_url); tracing::debug!("using token for {}", self.repo_url);
@ -412,7 +412,7 @@ impl IndexConfig {
pub fn download(&self) -> String { pub fn download(&self) -> String {
self.download self.download
.as_deref() .as_deref()
.unwrap_or("{API_URL}/v0/packages/{PACKAGE}/{PACKAGE_VERSION}/{PACKAGE_TARGET}") .unwrap_or("{API_URL}/v0/packages/{PACKAGE}/{PACKAGE_VERSION}/{PACKAGE_TARGET}/archive")
.replace("{API_URL}", self.api()) .replace("{API_URL}", self.api())
} }
} }

View file

@ -18,7 +18,7 @@ const fetchPackage = async (fetcher: typeof fetch, options: FetchPackageOptions)
try { try {
return await fetchRegistryJson<PackageVersionResponse>( return await fetchRegistryJson<PackageVersionResponse>(
`packages/${encodeURIComponent(`${scope}/${name}`)}/${version}/${target}`, `packages/${encodeURIComponent(`${scope}/${name}`)}/${encodeURIComponent(version)}/${encodeURIComponent(target)}`,
fetcher, fetcher,
) )
} catch (e) { } catch (e) {

View file

@ -11,7 +11,7 @@
const basePath = $derived.by(() => { const basePath = $derived.by(() => {
const { scope, name } = $page.params const { scope, name } = $page.params
return `/packages/${scope}/${name}` return `/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}`
}) })
const activeTab = $derived( const activeTab = $derived(

View file

@ -13,9 +13,9 @@
const { scope, name } = $page.params const { scope, name } = $page.params
if ("target" in $page.params) { if ("target" in $page.params) {
const { version } = $page.params const { version } = $page.params
return `/packages/${scope}/${name}/${version}` return `/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}`
} }
return `/packages/${scope}/${name}/latest` return `/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}/latest`
}) })
const items = ($page.data.pkg.targets as TargetInfo[]).map((target) => { const items = ($page.data.pkg.targets as TargetInfo[]).map((target) => {

View file

@ -54,13 +54,16 @@ export const load: LayoutLoad = async ({ params, url, fetch }) => {
if (version === undefined || version === "latest" || !isTargetKind(target)) { if (version === undefined || version === "latest" || !isTargetKind(target)) {
const pkg = await fetchRegistryJson<PackageVersionResponse>( const pkg = await fetchRegistryJson<PackageVersionResponse>(
`packages/${encodeURIComponent(`${scope}/${name}`)}/${version ?? "latest"}/${target ?? "any"}`, `packages/${encodeURIComponent(`${scope}/${name}`)}/${encodeURIComponent(version ?? "latest")}/${encodeURIComponent(target ?? "any")}`,
fetch, fetch,
) )
const path = url.pathname.split("/").slice(6).join("/") const path = url.pathname.split("/").slice(6).map(encodeURIComponent).join("/")
return redirect(303, `/packages/${scope}/${name}/${pkg.version}/${pkg.targets[0].kind}/${path}`) return redirect(
303,
`/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}/${encodeURIComponent(pkg.version)}/${encodeURIComponent(pkg.targets[0].kind)}/${path}`,
)
} }
const { pkg, versions } = await fetchPackageAndVersions(fetch, { scope, name, version, target }) const { pkg, versions } = await fetchPackageAndVersions(fetch, { scope, name, version, target })

View file

@ -13,6 +13,7 @@
import Toc from "./Toc.svelte" import Toc from "./Toc.svelte"
import TocObserver from "./TocObserver.svelte" import TocObserver from "./TocObserver.svelte"
import VersionSelector from "./VersionSelector.svelte" import VersionSelector from "./VersionSelector.svelte"
import { TARGET_KIND_DISPLAY_NAMES, type TargetInfo, type TargetKind } from "$lib/registry-api"
const { children, data } = $props() const { children, data } = $props()
const [scope, name] = data.pkg.name.split("/") const [scope, name] = data.pkg.name.split("/")
@ -84,7 +85,7 @@
<span class="flex min-w-0 items-center"> <span class="flex min-w-0 items-center">
<a <a
class="flex min-w-0 items-center" class="flex min-w-0 items-center"
href={`/packages/${scope}/${name}/${$page.params.version ?? "latest"}/${$page.params.target ?? "any"}`} href={`/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}/${encodeURIComponent($page.params.version ?? "latest")}/${encodeURIComponent($page.params.target ?? "any")}`}
> >
<span class="text-primary mr-2"> <span class="text-primary mr-2">
<Logomark class="h-7" /> <Logomark class="h-7" />
@ -98,6 +99,9 @@
<button <button
{...props} {...props}
class="flex items-center transition-opacity data-[disabled]:opacity-50" class="flex items-center transition-opacity data-[disabled]:opacity-50"
class:line-through={(
Object.entries($page.data.pkg.targets) as [TargetKind, TargetInfo][]
).find(([name]) => TARGET_KIND_DISPLAY_NAMES[name] === label)?.[1]?.yanked}
> >
{label} {label}
<ChevronsUpDown class="ml-1 size-4" /> <ChevronsUpDown class="ml-1 size-4" />

View file

@ -41,7 +41,7 @@
> >
<a <a
class="flex items-center truncate" class="flex items-center truncate"
href={`/packages/${scope}/${name}/${$page.params.version ?? "latest"}/${$page.params.target ?? "any"}`} href={`/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}/${encodeURIComponent($page.params.version ?? "latest")}/${encodeURIComponent($page.params.target ?? "any")}`}
> >
{#snippet separator()} {#snippet separator()}
<span class="text-body/60 px-2 text-xl">/</span> <span class="text-body/60 px-2 text-xl">/</span>

View file

@ -12,7 +12,7 @@
const basePath = $derived.by(() => { const basePath = $derived.by(() => {
const { scope, name, version, target } = $page.params const { scope, name, version, target } = $page.params
return `/packages/${scope}/${name}/${version ?? "latest"}/${target ?? "any"}` return `/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}/${encodeURIComponent(version ?? "latest")}/${encodeURIComponent(target ?? "any")}`
}) })
const href = $derived(`${basePath}/${tab}`) const href = $derived(`${basePath}/${tab}`)

View file

@ -18,7 +18,7 @@
const basePath = $derived.by(() => { const basePath = $derived.by(() => {
const { scope, name } = $page.params const { scope, name } = $page.params
return `/packages/${scope}/${name}` return `/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}`
}) })
</script> </script>

View file

@ -18,7 +18,7 @@
const basePath = $derived.by(() => { const basePath = $derived.by(() => {
const { scope, name } = $page.params const { scope, name } = $page.params
return `/packages/${scope}/${name}` return `/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}`
}) })
</script> </script>
@ -32,7 +32,7 @@
const path = $page.data.activeTab === "docs" ? "docs/intro" : "reference" const path = $page.data.activeTab === "docs" ? "docs/intro" : "reference"
fetchRegistryJson<PackageVersionResponse>( fetchRegistryJson<PackageVersionResponse>(
`packages/${encodeURIComponent($page.data.pkg.name)}/${version}/any`, `packages/${encodeURIComponent($page.data.pkg.name)}/${encodeURIComponent(version)}/any`,
fetch, fetch,
) )
.then((pkg) => goto(`${basePath}/${version}/${pkg.targets[0].kind}/${path}`)) .then((pkg) => goto(`${basePath}/${version}/${pkg.targets[0].kind}/${path}`))

View file

@ -6,7 +6,7 @@ export const load: LayoutLoad = async ({ params, parent }) => {
const parentData = await parent() const parentData = await parent()
const { scope, name, version, target } = params const { scope, name, version, target } = params
const basePath = `/packages/${scope}/${name}/${version ?? "latest"}/${target ?? "any"}` const basePath = `/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}/${encodeURIComponent(version ?? "latest")}/${encodeURIComponent(target ?? "any")}`
function docEntryToSidebarItem(entry: DocEntry): SidebarItem { function docEntryToSidebarItem(entry: DocEntry): SidebarItem {
if ("name" in entry) { if ("name" in entry) {

View file

@ -18,7 +18,7 @@ const findDocTitle = (docs: DocEntry[], name: string): string | undefined => {
export const load: PageLoad = async ({ params, parent, fetch }) => { export const load: PageLoad = async ({ params, parent, fetch }) => {
try { try {
const page = await fetchRegistry( const page = await fetchRegistry(
`packages/${encodeURIComponent(`${params.scope}/${params.name}`)}/${params.version ?? "latest"}/${params.target ?? "any"}?doc=${encodeURIComponent(params.doc)}`, `packages/${encodeURIComponent(`${params.scope}/${params.name}`)}/${encodeURIComponent(params.version ?? "latest")}/${encodeURIComponent(params.target ?? "any")}?doc=${encodeURIComponent(params.doc)}`,
fetch, fetch,
).then((r) => r.text()) ).then((r) => r.text())
@ -26,7 +26,7 @@ export const load: PageLoad = async ({ params, parent, fetch }) => {
path: `/docs/${params.doc}`, path: `/docs/${params.doc}`,
value: page, value: page,
data: { data: {
basePath: `/packages/${params.scope}/${params.name}/${params.version ?? "latest"}/${params.target ?? "any"}`, basePath: `/packages/${encodeURIComponent(params.scope)}/${encodeURIComponent(params.name)}/${encodeURIComponent(params.version ?? "latest")}/${encodeURIComponent(params.target ?? "any")}`,
}, },
}) })

View file

@ -2,7 +2,7 @@ import type { LayoutLoad } from "./$types"
export const load: LayoutLoad = async ({ params, parent }) => { export const load: LayoutLoad = async ({ params, parent }) => {
const { scope, name, version, target } = params const { scope, name, version, target } = params
const basePath = `/packages/${scope}/${name}/${version ?? "latest"}/${target ?? "any"}` const basePath = `/packages/${encodeURIComponent(scope)}/${encodeURIComponent(name)}/${encodeURIComponent(version ?? "latest")}/${encodeURIComponent(target ?? "any")}`
const parentData = await parent() const parentData = await parent()
return { return {

View file

@ -10,13 +10,8 @@ const fetchReadme = async (
) => { ) => {
try { try {
const res = await fetchRegistry( const res = await fetchRegistry(
`packages/${encodeURIComponent(name)}/${version}/${target}`, `packages/${encodeURIComponent(name)}/${encodeURIComponent(version)}/${encodeURIComponent(target)}/readme`,
fetcher, fetcher,
{
headers: {
Accept: "text/plain",
},
},
) )
return res.text() return res.text()

View file

@ -55,7 +55,7 @@
? { ? {
href: isWally href: isWally
? `https://wally.run/package/${stripWally(dependencyInfo.wally)}` ? `https://wally.run/package/${stripWally(dependencyInfo.wally)}`
: `/packages/${dependencyInfo.name}/latest/${target}`, : `/packages/${dependencyInfo.name}/latest/${encodeURIComponent(target)}`,
} }
: {}} : {}}
class="after:absolute after:inset-0 after:content-['']" class="after:absolute after:inset-0 after:content-['']"

View file

@ -22,7 +22,7 @@
> >
<h2 class="text-heading font-semibold"> <h2 class="text-heading font-semibold">
<a <a
href={`/packages/${data.name}/${pkgVersion.version}/any`} href={`/packages/${data.name}/${encodeURIComponent(pkgVersion.version)}/any`}
class="after:absolute after:inset-0 after:content-['']" class="after:absolute after:inset-0 after:content-['']"
> >
{pkgVersion.version} {pkgVersion.version}