openvibers/openvibe.network Commits

openvibe-shared v4.47.2 (was v4.43.0) (#429)

OP OpenVibers committed 14 days ago · 7a34300e06 · 7 files, +38 −12

* openvibe-shared v4.47.2 (was v4.43.0)

Opened by scripts/pin-bump.js in OpenVibers/OpenVibe.Shared after the v4.47.2 release.

* Updates: the first page groups the same change across sites

The server-rendered first page of /updates folds a day's entries that are
the same change into one row, with openvibe-shared/shipped.js's own
groupEntries (4.47.2), so it matches the page once the script runs: a pin
round is one row ('Shared 4.45.0 reached 31 sites') with its sites and
commits in a fold. The OpenVibers account is no longer named on each row.

* Home JS budget for openvibe-shared 4.47.2: 323.2 KB raw, 81.4 KB brotli

Shared grew between 4.43 and 4.47.2: the comfort settings in theme-loader
(4.45), touch targets (4.46) and the grouped updates log (4.47.2).

Changes

ddiff --git a/README.md b/README.md
iindex f69d12fc..e1205042 100644
---- a/README.md
++++ b/README.md
@@@ -785,5 +785,5 @@ MIT (`license` in [package.json](package.json)).
<!-- versions:start -->
- openvibe-contracts: v0.238.0
- openvibe-sdk: v0.67.0
--- openvibe-shared: v4.43.0
++- openvibe-shared: v4.47.2
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex 98a0b795..025b9002 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -8,7 +8,7 @@
"domain": "openvibe.network (with auth., api., admin., my. and themes.openvibe.network in its manifest)",
"contracts": "openvibe-contracts v0.238.0",
"sdk": "openvibe-sdk v0.67.0",
-- "shared": "openvibe-shared v4.43.0",
++ "shared": "openvibe-shared v4.47.2",
"capabilities": [
"identity.subject.resolve",
"mods.grant.manage",
@@@ -48,5 +48,5 @@
"per-actor limits (openvibe-sdk/limits)",
"universal telemetry: per-route aggregated platform.telemetry-sample@1 plus per-flush HTTP autoscaling gauges (openvibe-sdk/telemetry)"
],
-- "updated": "2026-10-29"
++ "updated": "2026-10-30"
}
ddiff --git a/package-lock.json b/package-lock.json
iindex 8f1fe496..aba02991 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -22,7 +22,7 @@
"multer": "^1.4.5-lts.1",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.238.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.43.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.47.2",
"pg": "^8.23.0",
"uuid": "^11.1.0",
"web-push": "^3.6.7"
@@@ -1639,9 +1639,9 @@
}
},
"node_modules/openvibe-shared": {
-- "version": "4.43.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.43.0",
-- "integrity": "sha512-OANEzCEB2QPgbkb9Yt0O67k6DxcK2pHMO1nhKYCjTI7OhUF8LW8EpLf6FJmpHmDTmpFTUq4pJ3lr3u2hPXMDYQ==",
++ "version": "4.47.2",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.47.2",
++ "integrity": "sha512-zsvjjCI3CxkjDiKlC4HHeiiE+W7DHb0tJTRoCe4aqCffN1SQVkwtoJf7UKWYP4jHCMjwHjjOAT+xhxAfXSCDuQ==",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0",
ddiff --git a/package.json b/package.json
iindex d4d45074..f28ebccf 100644
---- a/package.json
++++ b/package.json
@@@ -26,7 +26,7 @@
"multer": "^1.4.5-lts.1",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.238.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.43.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.47.2",
"pg": "^8.23.0",
"uuid": "^11.1.0",
"web-push": "^3.6.7"
ddiff --git a/server/updates/routes.js b/server/updates/routes.js
iindex 2149b6cb..ffbef591 100644
---- a/server/updates/routes.js
++++ b/server/updates/routes.js
@@@ -12,6 +12,7 @@ const cache = require('openvibe-shared/cache-policy');
*/
const express = require('express');
const boost = require('../boost');
++const shipped = require('openvibe-shared/shipped');
const TTL_MS = 60 * 1000;
const MAX_KEYS = 300;
@@@ -96,7 +97,19 @@ function renderPage({ site, filtered, body, releaseId = '' }) {
if (!days.length || days[days.length - 1].day !== d) days.push({ day: d, list: [] });
days[days.length - 1].list.push(e);
}
-- const dayHtml = days.map((g) => `<section class="ov-shipped-day"><h3>${esc(g.day)}</h3><div class="ov-shipped-entries">${g.list.map((e) => `<div class="ov-shipped-entry">${/^https:\/\//.test(e.url || '') ? `<a class="ov-shipped-hash" href="${esc(e.url)}" rel="noopener">${esc(e.short || String(e.sha || '').slice(0, 7))}</a>` : ''}${site ? '' : `<span class="ov-shipped-site">${esc(nameOf(e.service))}</span>`}<span class="ov-shipped-text">${esc(e.subject)}</span><span class="ov-shipped-meta">${esc(e.author ? `${e.author} · ` : '')}${esc(String(e.deployed_at || '').slice(11, 16))} UTC</span></div>`).join('')}</div></section>`).join('\n');
++ // One commit; the OpenVibers account every commit shares is not named on each row.
++ const hhmm = (e) => String(e.deployed_at || '').slice(11, 16);
++ const row = (e, showSite) => `<div class="ov-shipped-entry">${/^https:\/\//.test(e.url || '') ? `<a class="ov-shipped-hash" href="${esc(e.url)}" rel="noopener">${esc(e.short || String(e.sha || '').slice(0, 7))}</a>` : ''}${showSite ? `<span class="ov-shipped-site">${esc(nameOf(e.service))}</span>` : ''}<span class="ov-shipped-text">${esc(e.subject)}</span><span class="ov-shipped-meta">${esc(e.author && e.author !== 'OpenVibers' ? `${e.author} · ` : '')}${esc(hhmm(e))} UTC</span></div>`;
++ // The same change on many sites is one row (a pin round was thirty identical lines), grouped exactly as
++ // openvibe-shared/shipped.js groups the page once its script runs.
++ const groupHtml = (g) => {
++ if (g.entries.length === 1) return row(g.entries[0], !site);
++ const names = [...new Set(g.entries.map((e) => e.service))].map(nameOf);
++ const notes = shipped.groupNotes(g);
++ const first = hhmm(g.entries[g.entries.length - 1]), last = hhmm(g.entries[0]);
++ return `<div class="ov-shipped-entry ov-shipped-group"><span class="ov-shipped-count">${names.length}</span><span class="ov-shipped-text">${esc(shipped.groupTitle(g))}${notes ? ` <a class="ov-shipped-notes" href="${esc(notes)}" rel="noopener">What's in it</a>` : ''}</span><span class="ov-shipped-meta">${esc(first === last ? last : `${first}–${last}`)} UTC</span><details class="ov-shipped-fold"><summary>${esc(names.length > 7 ? `${names.slice(0, 6).join(', ')} and ${names.length - 6} more` : names.join(', '))}</summary><div class="ov-shipped-entries">${g.entries.map((e) => row(e, true)).join('')}</div></details></div>`;
++ };
++ const dayHtml = days.map((g) => `<section class="ov-shipped-day"><h3>${esc(g.day)}</h3><div class="ov-shipped-entries">${shipped.groupEntries(g.list).map(groupHtml).join('')}</div></section>`).join('\n');
const postsHtml = posts.length ? `<aside class="ov-shipped-posts"><h3>Patch notes</h3><ul>${posts.map((p) => `<li><a href="${esc(p.url)}">${esc(p.title || 'Patch notes')}</a>${p.published_at ? ` <small>${esc(String(p.published_at).slice(0, 10))}</small>` : ''}</li>`).join('')}</ul></aside>` : '';
// The no-JS site filter (links): inside the shipped mount, so shipped.js's own filter bar replaces it rather than
// adding a second row.
@@@ -128,6 +141,10 @@ a{color:var(--accent)}
.ov-shipped-entry{display:flex;gap:10px;align-items:baseline;padding:8px 12px;flex-wrap:wrap}.ov-shipped-text{flex:1;min-width:0;overflow-wrap:anywhere}
.ov-shipped-meta{font-size:.75rem;color:var(--text-secondary)}.ov-shipped-hash{font-family:ui-monospace,monospace;font-size:.8rem}
.ov-shipped-site{font-size:.72rem;font-weight:700;padding:.05rem .45rem;border-radius:999px;background:rgba(59,130,246,.16)}
++.ov-shipped-count{flex:none;min-width:2em;text-align:center;font-size:.72rem;font-weight:800;padding:.12rem .45rem;border-radius:999px;background:rgba(59,130,246,.22)}
++.ov-shipped-notes{font-size:.82rem;font-weight:600;text-decoration:none;white-space:nowrap}
++.ov-shipped-fold{flex-basis:100%;min-width:0;font-size:.82rem;color:var(--text-secondary)}.ov-shipped-fold summary{cursor:pointer;padding:3px 0}
++.ov-shipped-fold .ov-shipped-entries{margin:4px 0 2px;padding-left:10px;border-left:2px solid var(--border)}
</style>
</head>
<body>
ddiff --git a/test/perf-budget.test.js b/test/perf-budget.test.js
iindex 3a2637ae..c37f2d00 100644
---- a/test/perf-budget.test.js
++++ b/test/perf-budget.test.js
@@@ -15,8 +15,8 @@ const BUDGETS = {
htmlRawKB: 188, // 188 with the agent layer's rel=alternate/describedby link tags on every public page (SEO-1/SEO-2, 2026-10-29: +0.2 KB); 187 with Bench open (contracts 0.238.0, 2026-10-28); 186 with Cam open (contracts 0.237.0, 2026-10-28); 184.4 with Fun open (contracts 0.236.0, 2026-10-28); 182.7 with Vision and Exchange open (contracts 0.235.0, 2026-10-28); 179 with Meme open (contracts 0.234.0, 2026-10-28); 174.1 with Domains, Report and Design open (contracts 0.226-0.228, 2026-10-28); 168.1 the hero's orbit and rich cards for live streams, recordings and screenshots (2026-10-28: the owner asked for animated visuals and YouTube-style cards); 152.4 openvibe-shared 4.15 footer: the compact footer opens into the whole one, and the pulse line; 147.9 with the home's FAQ (six questions and their FAQPage JSON-LD), the numbers panel and the service map's group headings (2026-10-22); 141.2 with Tips and VIP open (contracts 0.200.0, 37 sites; the Opening next section is gone); 134 with Play (contracts 0.172.1); 128.3 with Events, AI and Search added (31 sites, contracts 0.135.0); 122.3 (shared 2.21.0: a real glyph for every product card, where ten drew the 'V'); was 110.6 with 33 live sites (contracts 0.119.0). The home page lists every site, so it grows with the network
htmlBrotliKB: 34, // 33.1 with Games granted its Bench match capabilities (BENCH-7, 2026-10-29: the home lists what each service may do); 32.0 with Fun, Vision and Exchange open (contracts 0.236.0, 2026-10-28); 31.2 with Domains, Report and Design open (2026-10-28); 30.1 with the Organization saying who OpenVibe is (description, other names, what it is not, 15 sites; 2026-10-28); 28.5 with the FAQ, numbers and groups (2026-10-22); 26.1 with Play; 25.1 (31 sites, contracts 0.135.0); 24.1 (shared 2.21.0, the same glyphs); 20.5 (contracts 0.116.0)
jsFiles: 8, // 7 with boost.js, was 6 (2026-10-13, plan T11 step 3: one more script on every boosted page; it loads route-transition.js on the first move only)
-- jsRawKB: 314, // 313.7 with openvibe-shared 4.43.0 (room.css carries the kit tokens, 4.39.1; the kit's 4.39.0 rules); 306.4 openvibe-shared 4.25.0: the menu's orb beside the brand, both balances on phones, messages, the account sheet on phones and the bar stepping away on phones; 298.9 openvibe-shared 4.22.0: the navbar's magnifier search button and More dropdown (search.js and loading.js load only when used); 293.1 Shared 4.17.0: OpenCoins and Vibes chips (wallet: true) in the navbar; 279.1 openvibe-shared 4.15 footer: the compact footer opens into the whole one, and the pulse line; 269.3 with shared 4.11.0's seasonal navbar (Pumpkin Night and the season badge, +2.6); 249.8 with boost.js (13.1); 228.3
-- jsBrotliKB: 79, // 78.9 with openvibe-shared 4.43.0 (room.css carries the kit tokens, 4.39.1; the kit's 4.39.0 rules); 77 openvibe-shared 4.25.0: the menu's orb beside the brand, both balances on phones, messages, the account sheet on phones and the bar stepping away on phones; 73.3 Shared 4.17.0: OpenCoins and Vibes chips (wallet: true) in the navbar; 70.7 openvibe-shared 4.15 footer: the compact footer opens into the whole one, and the pulse line; 68.0 with shared 4.12.0 (light/dark switch, seasonal mark, theme pairs); 64.5 with shared 4.11.0's navbar; 60.3 with boost.js (3.8); 53.9
++ jsRawKB: 324, // 323.2 with openvibe-shared 4.47.2 (the comfort settings in theme-loader 4.45, touch targets 4.46, the grouped updates log 4.47.2); 313.7 with openvibe-shared 4.43.0 (room.css carries the kit tokens, 4.39.1; the kit's 4.39.0 rules); 306.4 openvibe-shared 4.25.0: the menu's orb beside the brand, both balances on phones, messages, the account sheet on phones and the bar stepping away on phones; 298.9 openvibe-shared 4.22.0: the navbar's magnifier search button and More dropdown (search.js and loading.js load only when used); 293.1 Shared 4.17.0: OpenCoins and Vibes chips (wallet: true) in the navbar; 279.1 openvibe-shared 4.15 footer: the compact footer opens into the whole one, and the pulse line; 269.3 with shared 4.11.0's seasonal navbar (Pumpkin Night and the season badge, +2.6); 249.8 with boost.js (13.1); 228.3
++ jsBrotliKB: 82, // 81.4 with openvibe-shared 4.47.2 (comfort settings, touch targets, the grouped updates log); 78.9 with openvibe-shared 4.43.0 (room.css carries the kit tokens, 4.39.1; the kit's 4.39.0 rules); 77 openvibe-shared 4.25.0: the menu's orb beside the brand, both balances on phones, messages, the account sheet on phones and the bar stepping away on phones; 73.3 Shared 4.17.0: OpenCoins and Vibes chips (wallet: true) in the navbar; 70.7 openvibe-shared 4.15 footer: the compact footer opens into the whole one, and the pulse line; 68.0 with shared 4.12.0 (light/dark switch, seasonal mark, theme pairs); 64.5 with shared 4.11.0's navbar; 60.3 with boost.js (3.8); 53.9
cssFiles: 2, // 1 (Font Awesome, cross-origin)
externalFiles: 3, // 1 locally; production adds Cloudflare's beacon
};
ddiff --git a/test/updates.test.js b/test/updates.test.js
iindex 18c11090..c13fc515 100644
---- a/test/updates.test.js
++++ b/test/updates.test.js
@@@ -5,7 +5,7 @@
const assert = require('assert');
const http = require('http');
const express = require('express');
--const { createUpdatesRoutes, serviceFor } = require('../server/updates/routes');
++const { createUpdatesRoutes, serviceFor, renderPage } = require('../server/updates/routes');
const publicCors = require('../server/public-cors');
(async () => {
@@@ -55,6 +55,15 @@ const publicCors = require('../server/public-cors');
assert.strictEqual(serviceFor('search.openvibe.network'), 'search');
assert.strictEqual(serviceFor('evil.example'), null);
assert.ok(publicCors.isPublicDiscoveryPath('/api/v1/changelog'), 'readable from any origin');
++
++ // The first page groups the same change across sites, as openvibe-shared/shipped.js (4.47.2) does once it runs.
++ const at = '2026-10-29T17:40:00.000Z';
++ const pin = (svc, n) => ({ service: svc, sha: String(n).repeat(40).slice(0, 40), subject: `openvibe-shared v4.45.0 (was v4.43.0) (#${n})`, author: 'OpenVibers', deployed_at: at, url: `https://github.com/OpenVibers/x/commit/${n}` });
++ const grouped = renderPage({ site: null, filtered: false, body: { entries: [pin('fun', 9), pin('chat', 252), pin('cam', 7), { service: 'trade', sha: 'c'.repeat(40), subject: 'Touch targets on phones (fleet audit) (#141)', author: 'OpenVibers', deployed_at: at, url: 'https://github.com/OpenVibers/x/commit/c' }] } });
++ assert.ok(grouped.includes('<span class="ov-shipped-count">3</span><span class="ov-shipped-text">Shared 4.45.0 reached 3 sites'), 'one row for a pin round');
++ assert.ok(grouped.includes('https://github.com/OpenVibers/OpenVibe.Shared/blob/v4.45.0/CHANGELOG.md'), "what's in that release");
++ assert.strictEqual(grouped.split('class="ov-shipped-entry"').length - 1, 4, 'every commit still listed in its fold, plus the single one');
++ assert.ok(!grouped.includes('OpenVibers · '), 'the shared account is not named on each row');
srv.close();
console.log('updates: all checks passed');
})().catch((e) => { console.error(e); process.exit(1); });