OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 commits / 317d7a1

0.1.245: hide registry entries this forge cannot serve, behind ?all=1
0.1.243 marked them; they were still rendered. On hyper-tps that is 377 of 384 rows -- they buried the 7 usable versions and shipped ~280KB of markup whose only message was "you cannot install this from here". They are now OMITTED, not merely dimmed, so the page weight goes with them. A line above the table states the count and links to ?all=1, which renders them with the existing marker and still refuses to print an install command. The route already strips the query before matching and parses it separately, so no routing change was needed beyond passing the flag through. Nothing is hidden without saying so: the count and the link are always shown when any entry is unservable, and never shown when none is. Also fixes a flaw in my own 0.1.243 tests: they called renderRepoReleases(svc, 'demo', {}) -- ctx and repoName swapped -- and passed anyway because the install command falls back to e.name. Corrected to the router's real (svc, ctx, repoName, opts) order, and the summary assertion now checks the COUNT rather than the exact sentence, which is not the contract. Pre-existing and unrelated: test/release-overlay-repair.test.js fails 3/6. 13/14 suites pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
317d7a1dcb771441924a67128e8ccbe5b1cb92a9
by plankatron <fleeky@gmail.com> · 2026-08-29T20:35:09.000Z

Changed files (4) +57 -9

lib/web/pages/repo-releases.js +13 -4
……@@ -53,7 +53,7 @@ function timeAgo (ms) {
5353 return Math.floor(mo / 12) + 'y ago'
5454}
5555
56export async function renderRepoReleases (svc, ctx, repoName) {
56export async function renderRepoReleases (svc, ctx, repoName, opts = {}) {
5757 const prefix = (ctx && ctx.prefix) || ''
5858 if (!svc._releases) {
5959 return errorPage('Release manager not initialised', ctx, repoName)
……@@ -90,7 +90,12 @@ export async function renderRepoReleases (svc, ctx, repoName) {
9090 .map(e => ({ ...e, _ms: toMs(e.publishedAt) }))
9191 .sort((a, b) => b._ms - a._ms)
9292
93 // Entries this forge cannot serve are OMITTED by default, not just dimmed.
94 // On hyper-tps 377 of 384 rows were unservable: they buried the 7 usable ones
95 // and shipped ~280KB of markup to say so. `?all=1` renders them, marked.
96 const showAll = !!opts.showAll
9397 const absentCount = treeEntries.filter(e => e.heldLocally === false).length
98 const shownEntries = showAll ? treeEntries : treeEntries.filter(e => e.heldLocally !== false)
9499
95100 const { versions, channels } = await svc._releases.list(repoName)
96101 const pinnedVersions = new Set(Object.values(channels || {}).map(c => c.version))
……@@ -123,19 +128,23 @@ ${renderRepoTabs(repoName, 'releases', {}, prefix)}
123128 </div>
124129 ${absentCount > 0
125130 ? `<div class="meta" style="margin-bottom:0.6rem">
126 <strong>${absentCount}</strong> of ${treeEntries.length} not stored on this forge.
131 <strong>${absentCount}</strong> of ${treeEntries.length} entries are not stored on this
132 forge and are ${showAll ? 'shown below, marked' : 'hidden'}.
127133 The registry is append-only; the tree store is garbage-collected, so older
128134 entries outlive their bytes. Another peer may still hold them.
135 ${showAll
136 ? `<a href="${base}/releases">show only what this forge serves</a>`
137 : `<a href="${base}/releases?all=1">show all ${treeEntries.length}</a>`}
129138 </div>`
130139 : ''}
131140 ${treeRows.unavailable
132141 ? `<div class="empty">Registry unavailable — ${escapeHtml(treeRows.unavailable)}</div>`
133 : treeEntries.length === 0
142 : shownEntries.length === 0
134143 ? `<div class="empty">Nothing published to pgh under the name <code>${escapeHtml(repoName)}</code>.</div>`
135144 : `<table class="version-table">
136145 <thead><tr><th>Version</th><th>Published</th><th>Deps</th><th>Tree</th><th>Install</th></tr></thead>
137146 <tbody>
138 ${treeEntries.map(e => `<tr${e.heldLocally === false ? ' class="tree-absent"' : ''}>
147 ${shownEntries.map(e => `<tr${e.heldLocally === false ? ' class="tree-absent"' : ''}>
139148 <td><strong>${escapeHtml(String(e.version || ''))}</strong>${e.runtime ? ` <span class="meta">${escapeHtml(e.runtime)}</span>` : ''}${
140149 e.heldLocally === false
141150 ? ' <span class="badge badge-closed" title="This forge does not hold this tree. The registry is append-only while the tree store is garbage-collected, so older entries routinely outlive their bytes. Another peer may still hold it.">not stored here</span>'
lib/web/router.js +1 -1
……@@ -1091,7 +1091,7 @@ export function registerRoutes (svc) {
10911091 const repoReleasesMatch = url.match(/^\/repos\/([^/]+)\/releases$/)
10921092 if (repoReleasesMatch) {
10931093 const repoName = decodeURIComponent(repoReleasesMatch[1])
1094 return sendHtml(res, await renderRepoReleases(svc, ctx, repoName))
1094 return sendHtml(res, await renderRepoReleases(svc, ctx, repoName, { showAll: query.all === '1' }))
10951095 }
10961096
10971097 // /repos/:name/docs — markdown docs index (U38)
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "pear-git-v3",
3 "version": "0.1.244",
3 "version": "0.1.245",
44 "description": "P2P git forge — multi-writer issue/org/release/peer-mirror daemon. Built bottom-up after the failed v2.",
55 "type": "module",
66 "main": "index.js",
test/registry-held-locally.test.js +42 -3
……@@ -83,14 +83,15 @@ function svcWith (entries) {
8383}
8484
8585test('the page marks absent trees and refuses to print an install for them', async (t) => {
86 // (svc, ctx, repoName, opts) -- the order the router actually uses.
8687 const html = await renderRepoReleases(svcWith([
8788 { name: 'demo', version: '2.0.0', treeHash: 'aa'.repeat(32), publishedAt: 2, heldLocally: true },
8889 { name: 'demo', version: '1.0.0', treeHash: 'bb'.repeat(32), publishedAt: 1, heldLocally: false }
89 ]), 'demo', {})
90 ]), {}, 'demo', { showAll: true })
9091
9192 t.ok(html.includes('not stored here'), 'the absent row is labelled')
9293 t.ok(html.includes('tree-absent'), 'and dimmed via its own class')
93 t.ok(html.includes('1</strong> of 2 not stored on this forge'), 'summary counts it')
94 t.ok(html.includes('<strong>1</strong> of 2'), 'summary counts it (copy is not the contract)')
9495
9596 // The held version keeps a usable command; the absent one must not have one.
9697 t.ok(html.includes('pgh app install pgh://' + 'k'.repeat(64) + '/demo@2.0.0'),
……@@ -103,9 +104,47 @@ test('the page marks absent trees and refuses to print an install for them', asy
103104test('a fully-held list shows no summary and no markers', async (t) => {
104105 const html = await renderRepoReleases(svcWith([
105106 { name: 'demo', version: '2.0.0', treeHash: 'aa'.repeat(32), publishedAt: 2, heldLocally: true }
106 ]), 'demo', {})
107 ]), {}, 'demo', {})
107108
108109 t.absent(html.includes('not stored here'), 'no marker')
109110 t.absent(html.includes('not stored on this forge'), 'no summary noise when everything resolves')
110111 t.ok(html.includes('pgh app install'), 'install still offered')
111112})
113
114// ── Hidden by default ────────────────────────────────────────
115//
116// 377 of 384 hyper-tps rows were entries this forge cannot serve. Rendering
117// them by default buried the 7 usable ones and shipped ~280KB of markup to say
118// so. They are OMITTED now, not merely dimmed, with a link to see them.
119
120test('not-stored entries are omitted by default', async (t) => {
121 const html = await renderRepoReleases(svcWith([
122 { name: 'demo', version: '2.0.0', treeHash: 'aa'.repeat(32), publishedAt: 2, heldLocally: true },
123 { name: 'demo', version: '1.0.0', treeHash: 'bb'.repeat(32), publishedAt: 1, heldLocally: false },
124 { name: 'demo', version: '0.9.0', treeHash: 'cc'.repeat(32), publishedAt: 0, heldLocally: false }
125 ]), {}, 'demo', {})
126
127 t.ok(html.includes('demo@2.0.0'), 'the servable version is shown')
128 t.absent(html.includes('0.9.0'), 'a not-stored version is not rendered at all')
129 t.absent(html.includes('1.0.0'), 'nor the other one')
130 t.ok(html.includes('?all=1'), 'but there is a way to see them')
131 t.ok(/2 (older )?entr/i.test(html) || html.includes('>2<'), 'and the count is stated')
132})
133
134test('showAll renders them, marked', async (t) => {
135 const html = await renderRepoReleases(svcWith([
136 { name: 'demo', version: '2.0.0', treeHash: 'aa'.repeat(32), publishedAt: 2, heldLocally: true },
137 { name: 'demo', version: '1.0.0', treeHash: 'bb'.repeat(32), publishedAt: 1, heldLocally: false }
138 ]), {}, 'demo', { showAll: true })
139
140 t.ok(html.includes('1.0.0'), 'the not-stored version appears')
141 t.ok(html.includes('not stored here'), 'still marked')
142 t.absent(html.includes('/demo@1.0.0'), 'and still offers no install')
143})
144
145test('when everything is servable there is no hidden-count line', async (t) => {
146 const html = await renderRepoReleases(svcWith([
147 { name: 'demo', version: '2.0.0', treeHash: 'aa'.repeat(32), publishedAt: 2, heldLocally: true }
148 ]), {}, 'demo', {})
149 t.absent(html.includes('?all=1'), 'no toggle offered when nothing is hidden')
150})