openvibers/openvibe.shared Commits

1.11.1: the footer's shipped line follows the footer's own service (a local or unmapped host showed the whole network); an id that is not a registry id falls back to the hostname

OP OpenVibers committed 24 Sept 2026 · d2dbacb76f · 6 files, +21 −6


Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex f5f3f10..dc122ae 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,10 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
breaking change to any exported module, browser global or served file name is a new major.
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
++## 1.11.1 — 2026-09-24
++
++Fix: the footer's "shipped X ago" line uses the footer's own `service` (`live`, `wiki`, …). Before, it resolved the page's hostname, so a local or unmapped host showed the whole network. A site id that is not a registry id (a Tools satellite's `dev`) falls back to the hostname rather than to the whole network.
++
## 1.11.0 — 2026-09-24
**The OpenVibe Frame.** The shared navbar, footer and "shipped" views every OpenVibe site sits in were called the "chrome", which was borrowed jargon easily mistaken for the browser. They are now the Frame:
ddiff --git a/footer.js b/footer.js
iindex c70c965..9adbb1a 100644
---- a/footer.js
++++ b/footer.js
@@@ -191,7 +191,7 @@
if (c.shipped === false) return '';
const host = typeof location !== 'undefined' ? location.hostname : '';
const updates = c.updates || `${NETWORK_URL}/updates?site=${encodeURIComponent(host || c.service || '')}`;
-- return `<a class="ovf-shipped" data-ov-shipped="latest" data-service="auto" href="${esc(updates)}" hidden></a>`;
++ return `<a class="ovf-shipped" data-ov-shipped="latest" data-service="${esc(c.service || 'auto')}" href="${esc(updates)}" hidden></a>`;
}
function updatesHref(c) {
const host = typeof location !== 'undefined' ? location.hostname : '';
ddiff --git a/package-lock.json b/package-lock.json
iindex 4c9bb67..435124b 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-shared",
-- "version": "1.11.0",
++ "version": "1.11.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-shared",
-- "version": "1.11.0",
++ "version": "1.11.1",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0"
ddiff --git a/package.json b/package.json
iindex 6070eb1..5dde550 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-shared",
-- "version": "1.11.0",
++ "version": "1.11.1",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"exports": {
ddiff --git a/shipped.js b/shipped.js
iindex b187cc9..6056b70 100644
---- a/shipped.js
++++ b/shipped.js
@@@ -124,9 +124,11 @@
const m = /(?:^|\.)openvibe\.([a-z]+)$/.exec(h);
return m && SITE_NAMES[m[1]] ? m[1] : null;
}
++ /** 'auto' = this page's host; a site id that is not a registry id (a Tools satellite's 'dev') also falls back to the host; '' = the whole network. */
function resolveService(s) {
-- if (s === 'auto') return typeof location !== 'undefined' ? serviceFor(location.hostname) : null;
-- return s ? serviceFor(s) : null;
++ const host = () => (typeof location !== 'undefined' ? serviceFor(location.hostname) : null);
++ if (s === 'auto') return host();
++ return s ? (serviceFor(s) || host()) : null;
}
// One request per feed URL for 30 s, shared by the footer line, the pill and the list on a page.
ddiff --git a/test/shipped.test.js b/test/shipped.test.js
iindex b763208..4abed92 100644
---- a/test/shipped.test.js
++++ b/test/shipped.test.js
@@@ -88,6 +88,15 @@ const data = {
assert.strictEqual(sf('www.openvibe.live'), 'live');
assert.strictEqual(sf('example.com'), null);
assert.strictEqual(sf('live'), 'live');
++ // A footer's own site id wins over the page's host; an id that is not a registry id falls back to the host.
++ globalThis.location = { hostname: 'dev.openvibe.tools' };
++ const own = document.createElement('a');
++ await shipped.latest(own, { service: 'live', api: 'https://example.test/feed' });
++ globalThis.fetch = async (url) => ({ ok: true, json: async () => ({ entries: [{ service: new URL(url).searchParams.get('service') || 'all', subject: 's', deployed_at: new Date().toISOString() }] }) });
++ const sat = document.createElement('a');
++ await shipped.latest(sat, { service: 'dev', api: 'https://example.test/feed2' });
++ assert.ok(sat.textContent.includes('shipped') && !sat.textContent.includes('all shipped'), 'a satellite id resolves to tools by host');
++ delete globalThis.location;
// log(): pages with the cursor, and the "Load more" button disappears on the last page.
const calls = [];