Skip to content

Commit 696607a

Browse files
authored
feat: configurable provenance badge and dependency provenance donut (#165)
1 parent 483819a commit 696607a

11 files changed

Lines changed: 90 additions & 11 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Migrate the Node Modules Inspector to use `devframe` as the underlying framework
2525
- [2026-05-19] Enhance maintainer actions view with catalog resolution and filtering - Resolve catalogs for workspace packages (e.g., "catalog:deps -> v0.2.16"), group results by package, and add filtering capability by package maintainers to improve actionability and organization of the view.
2626
- [2026-05-21] Refactor dependency upgrade view UI - Replace side panel with right-drawer layout, display version changes in table format for better alignment, remove "Message to Maintainer" feature, and refocus details page on dependency upgrade decisions rather than package-centric view.
2727
- [2026-05-21] Ignore monorepo dependencies in deps update actions - Exclude dependencies that share the same repository link from dependency update actions (e.g., nuxt and @nuxt/kit from the same monorepo) to avoid redundant or confusing update suggestions.
28+
- [2026-05-23] Consolidate provenance badge UI strategy - Move provenance indicator from main package view to "deps on" tab; replace donut visualization with progress bar (matching ESM/CJS style); add packageSpec badge slot for inline badge placement within version indicator parts; display provenance badge in the dependency tree within "deps on" tab for better information density and consistency.
2829

2930
### Open Questions
3031
- Which parts of the codebase need to be refactored vs. kept as-is?

packages/node-modules-inspector/src/app/components/display/PackageSpec.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ const vulnerability = computed(() => getVulnerability(props.pkg))
4141
'op-fade': !deprecation?.current && !vulnerability,
4242
}"
4343
/>
44+
<slot />
4445
</span>
4546
</template>

packages/node-modules-inspector/src/app/components/display/ProvenanceBadge.vue

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,32 @@ import type { PackageNode } from 'node-modules-tools'
33
import { Tooltip } from 'floating-vue'
44
import { computed } from 'vue'
55
import { getNpmMeta } from '../../state/payload'
6+
import { settings } from '../../state/settings'
67
78
const props = defineProps<{
89
pkg: PackageNode
10+
class?: string
911
}>()
1012
1113
const meta = computed(() => getNpmMeta(props.pkg))
1214
</script>
1315

1416
<template>
15-
<Tooltip v-if="meta?.provenance">
16-
<div i-ph:circle-wavy-check-duotone text-primary-400 text-sm />
17-
<template #popper>
18-
This package is built and signed
19-
{{ meta.provenance === 'trustedPublisher' ? 'by trusted publisher' : 'with provenance' }}
20-
</template>
21-
</Tooltip>
17+
<template v-if="settings.showProvenanceBadge === 'present'">
18+
<Tooltip v-if="meta?.provenance" inline-flex :class="props.class">
19+
<div i-ph:circle-wavy-check-duotone ma h-1.1em text-primary-400 />
20+
<template #popper>
21+
This package is built and signed
22+
{{ meta.provenance === 'trustedPublisher' ? 'by trusted publisher' : 'with provenance' }}
23+
</template>
24+
</Tooltip>
25+
</template>
26+
<template v-else-if="settings.showProvenanceBadge === 'absent'">
27+
<Tooltip v-if="meta && !meta.provenance" inline-flex :class="props.class">
28+
<div i-ph:circle-wavy-warning-duotone ma h-1.1em text-amber-400 />
29+
<template #popper>
30+
This package is not signed with provenance
31+
</template>
32+
</Tooltip>
33+
</template>
2234
</template>

packages/node-modules-inspector/src/app/components/grid/Item.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ defineProps<{
1818
@click="selectedNode = pkg === selectedNode ? undefined : pkg"
1919
>
2020
<div flex="~ gap-2 items-center" text-left>
21-
<DisplayPackageSpec :pkg />
22-
<DisplayProvenanceBadge :pkg />
21+
<DisplayPackageSpec :pkg>
22+
<DisplayProvenanceBadge :pkg class="translate-x-1 translate-y-0.55" />
23+
</DisplayPackageSpec>
2324
</div>
2425
<div flex="~ wrap gap-2 items-center" text-sm>
2526
<DisplayModuleType :pkg />

packages/node-modules-inspector/src/app/components/panel/PackageDetails.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,13 @@ const thirdPartyServices = computed(() => {
420420
:pkg="pkg"
421421
:flat="settings.deepDependenciesTree"
422422
/>
423+
<div op-fade text-sm mt2>
424+
Dependency Provenance
425+
</div>
426+
<UiPercentageProvenance
427+
:pkg="pkg"
428+
:flat="settings.deepDependenciesTree"
429+
/>
423430
</div>
424431

425432
<template v-if="payloads.available.flatDependencies(pkg).length">

packages/node-modules-inspector/src/app/components/panel/PackageDetailsInfo.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ function showDuplicatedGraph(pkgs: PackageNode[]) {
4949
font-mono text-2xl flex="~ wrap items-center gap-2"
5050
:class="deprecation?.latest ? deprecation.type === 'future' ? 'text-orange line-through' : 'text-red line-through' : ''"
5151
/>
52-
5352
<DisplayProvenanceBadge :pkg />
5453
</div>
5554

packages/node-modules-inspector/src/app/components/panel/Settings.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ const backend = getBackend()
2828
<OptionItem title="Show publish time badge" description="Show publish time badge on package list">
2929
<OptionCheckbox v-model="settings.showPublishTimeBadge" />
3030
</OptionItem>
31+
<OptionItem title="Provenance badge" description="Show provenance indicator on packages">
32+
<OptionSelectGroup
33+
v-model="settings.showProvenanceBadge"
34+
:options="['present', 'absent', 'none']"
35+
:titles="['Present', 'Absent', 'None']"
36+
/>
37+
</OptionItem>
3138
<OptionItem title="Colorize size badge" description="Colorize package size badge">
3239
<OptionCheckbox v-model="settings.colorizePackageSize" />
3340
</OptionItem>

packages/node-modules-inspector/src/app/components/tree/Item.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ withDefaults(
2626
<slot name="before" />
2727
<DisplayModuleType v-if="showModuleType" :pkg />
2828
<DisplaySourceTypeBadge v-if="showSourceType" :pkg />
29-
<DisplayPackageSpec :pkg />
29+
<DisplayPackageSpec :pkg>
30+
<DisplayProvenanceBadge :pkg class="translate-x-1 translate-y-0.55" />
31+
</DisplayPackageSpec>
3032
<slot name="after" />
3133
</button>
3234
</template>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<script setup lang="ts">
2+
import type { PackageNode } from 'node-modules-tools'
3+
import { computed } from 'vue'
4+
import { getNpmMeta, payloads } from '../../state/payload'
5+
6+
const props = withDefaults(
7+
defineProps<{
8+
pkg?: PackageNode
9+
packages?: PackageNode[]
10+
flat?: boolean
11+
rounded?: boolean
12+
}>(),
13+
{
14+
flat: false,
15+
rounded: true,
16+
},
17+
)
18+
19+
const nodes = computed(() => {
20+
const pkgs = props.pkg
21+
? [
22+
props.pkg,
23+
...props.flat
24+
? payloads.available.flatDependencies(props.pkg)
25+
: payloads.available.dependencies(props.pkg),
26+
]
27+
: props.packages ?? []
28+
29+
let signed = 0
30+
let unsigned = 0
31+
for (const p of pkgs) {
32+
if (getNpmMeta(p)?.provenance)
33+
signed++
34+
else
35+
unsigned++
36+
}
37+
38+
return [
39+
{ value: signed, name: 'SIGNED', class: 'badge-color-green', title: `${signed} dependencies signed with provenance` },
40+
{ value: unsigned, name: 'UNSIGNED', class: 'badge-color-gray', title: `${unsigned} dependencies not signed with provenance` },
41+
].filter(n => n.value > 0)
42+
})
43+
</script>
44+
45+
<template>
46+
<UiPercentage :nodes :rounded />
47+
</template>

packages/node-modules-inspector/src/app/state/settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const settings = useLocalStorage<SettingsOptions>(
1212
colorizePackageSize: true,
1313
showInstallSizeBadge: true,
1414
showPublishTimeBadge: false,
15+
showProvenanceBadge: 'present',
1516
showFileComposition: false,
1617
showDependencySourceBadge: 'dev',
1718
showPublintMessages: false,

0 commit comments

Comments
 (0)