Skip to content

Commit e6bb3e2

Browse files
committed
feat(package): add provenance and popover to package
1 parent 2c99239 commit e6bb3e2

1 file changed

Lines changed: 88 additions & 14 deletions

File tree

app/pages/[...package].vue

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<script setup lang="ts">
2-
import type { NpmVersionDist, PackumentVersion, ReadmeResponse } from '#shared/types'
2+
import type {
3+
NpmVersionDist,
4+
PackumentVersion,
5+
ProvenanceDetails,
6+
ReadmeResponse,
7+
} from '#shared/types'
38
import type { JsrPackageInfo } from '#shared/types/jsr'
49
import { assertValidPackageName } from '#shared/utils/npm'
510
import { onKeyStroke } from '@vueuse/core'
@@ -108,6 +113,33 @@ watch(
108113
},
109114
)
110115
116+
// Fetch provenance details when the displayed version has attestations
117+
const {
118+
data: provenanceData,
119+
status: provenanceStatus,
120+
execute: fetchProvenance,
121+
} = useLazyFetch<ProvenanceDetails | null>(
122+
() => {
123+
const v = displayVersion.value
124+
if (!v || !hasProvenance(v)) return ''
125+
return `/api/registry/provenance/${packageName.value}/v/${v.version}`
126+
},
127+
{
128+
default: () => null,
129+
server: false,
130+
immediate: false,
131+
},
132+
)
133+
watch(
134+
() => [displayVersion.value?.version, hasProvenance(displayVersion.value)],
135+
() => {
136+
if (displayVersion.value && hasProvenance(displayVersion.value)) {
137+
fetchProvenance()
138+
}
139+
},
140+
{ immediate: true },
141+
)
142+
111143
// Keep latestVersion for comparison (to show "(latest)" badge)
112144
const latestVersion = computed(() => {
113145
if (!pkg.value) return null
@@ -408,19 +440,40 @@ function handleClick(event: MouseEvent) {
408440
>
409441
<span v-else>v{{ displayVersion.version }}</span>
410442

411-
<a
412-
v-if="hasProvenance(displayVersion)"
413-
:href="`https://www.npmjs.com/package/${pkg.name}/v/${displayVersion.version}#provenance`"
414-
target="_blank"
415-
rel="noopener noreferrer"
416-
class="inline-flex items-center justify-center gap-1.5 text-fg-muted hover:text-fg transition-colors duration-200 min-w-6 min-h-6"
417-
:title="$t('package.verified_provenance')"
418-
>
419-
<span
420-
class="i-solar:shield-check-outline w-3.5 h-3.5 shrink-0"
421-
aria-hidden="true"
422-
/>
423-
</a>
443+
<AppPopover v-if="hasProvenance(displayVersion)" position="bottom">
444+
<template #content>
445+
<p class="flex items-center gap-2 text-fg m-0">
446+
<span
447+
class="i-solar-shield-check-outline w-3.5 h-3.5 shrink-0 text-emerald-500"
448+
aria-hidden="true"
449+
/>
450+
<span>{{
451+
provenanceData
452+
? $t('package.provenance_section.built_and_signed_on', {
453+
provider: provenanceData.providerLabel,
454+
})
455+
: $t('package.verified_provenance')
456+
}}</span>
457+
</p>
458+
<a href="#provenance" class="block mt-1.5 link font-medium">
459+
{{ $t('package.provenance_section.view_more_details') }}
460+
</a>
461+
</template>
462+
<template #default="{ popoverVisible }">
463+
<a
464+
:href="`https://www.npmjs.com/package/${pkg.name}/v/${displayVersion.version}#provenance`"
465+
target="_blank"
466+
rel="noopener noreferrer"
467+
class="inline-flex items-center justify-center gap-1.5 text-fg-muted hover:text-emerald-500 transition-colors duration-200 min-w-6 min-h-6"
468+
:class="popoverVisible && 'text-emerald-500'"
469+
>
470+
<span
471+
class="i-solar-shield-check-outline w-3.5 h-3.5 shrink-0"
472+
aria-hidden="true"
473+
/>
474+
</a>
475+
</template>
476+
</AppPopover>
424477
<span
425478
v-if="
426479
requestedVersion &&
@@ -897,6 +950,27 @@ function handleClick(event: MouseEvent) {
897950
$t('package.readme.view_on_github')
898951
}}</a>
899952
</p>
953+
954+
<!-- Provenance details (when version has attestations) -->
955+
<template v-if="hasProvenance(displayVersion)">
956+
<div
957+
v-if="provenanceStatus === 'pending'"
958+
class="mt-8 flex items-center gap-2 text-fg-subtle text-sm"
959+
>
960+
<span
961+
class="i-carbon-circle-dash w-4 h-4 motion-safe:animate-spin"
962+
aria-hidden="true"
963+
/>
964+
<span>{{ $t('package.provenance_section.title') }}…</span>
965+
</div>
966+
<PackageProvenanceSection
967+
v-else-if="provenanceData"
968+
:details="provenanceData"
969+
:package-name="pkg.name"
970+
:version="displayVersion?.version"
971+
class="mt-8"
972+
/>
973+
</template>
900974
</section>
901975

902976
<div class="area-sidebar">

0 commit comments

Comments
 (0)