openvibers/openvibe.search Commits

Search moves between its pages without a reload (plan T11): openvibe-shared v2.2.0 boost swaps <main> styles-first with the shared transition and does a normal load across a deploy (the ov-boost marker carries the release); page scripts that wire <main> also run on ov:boost:load. Tests pin the marker and the boost tag (verified).

OP OpenVibers committed 29 Sept 2026 · eea0abe7f0 · 5 files, +24 −7


Changes

ddiff --git a/package-lock.json b/package-lock.json
iindex 8093f5a..b90f2bb 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -15,7 +15,7 @@
"express": "^4.21.2",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.76.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.23.1",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.27.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.2.0",
"pg": "^8.23.0"
},
"devDependencies": {
@@@ -1069,9 +1069,9 @@
}
},
"node_modules/openvibe-shared": {
-- "version": "1.27.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.27.0",
-- "integrity": "sha512-/H7dyFw+cURcdjyfbpyY7mNc6F+Pcb9TwkVwZHR+9d3M62q70KDhZLo/gZi71eaYxUjD8lO8tmbXu70EAa9paQ==",
++ "version": "2.2.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.2.0",
++ "integrity": "sha512-1q+iUuUxzPeylTYmithW4jR++Jccvn5GEOrB1B3D18Dgn6OXQhPpvhPQAa1D1k1p+dWIa1/SmggyJwSGFnTNtw==",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0"
ddiff --git a/package.json b/package.json
iindex 86cd490..a0f1544 100644
---- a/package.json
++++ b/package.json
@@@ -27,7 +27,7 @@
"express": "^4.21.2",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.76.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.23.1",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.27.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.2.0",
"pg": "^8.23.0"
},
"engines": {
ddiff --git a/server/app.js b/server/app.js
iindex 930aeb0..32aa268 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -20,6 +20,7 @@ function createApp({ config, db, store, engine, auth, outbox, relay, purges, pur
app.set('trust proxy', 'loopback');
app.set('query parser', 'extended');
const release = createRelease({ service: 'search', root: path.join(__dirname, '..') });
++ require('./web/page').setRelease(release.release);
// HTTP golden signals by route template, process metrics, release_info and the Search gauges;
// GET /metrics answers direct loopback callers only (Track O).
const metrics = instrument(app, { service: 'search', release: release.release });
ddiff --git a/server/web/page.js b/server/web/page.js
iindex 392a829..7a8c30a 100644
---- a/server/web/page.js
++++ b/server/web/page.js
@@@ -21,6 +21,11 @@ const { QueryError, one } = require('../api/query');
const SITE_NAME = 'OpenVibe.Search';
const SITE_DESCRIPTION = "Search what the OpenVibe network's services have published.";
++// The deployed release (app.js sets it from openvibe-shared/release): openvibe-shared/boost swaps a page in
++// place only between pages of the same release, and does a normal load across a deploy.
++let RELEASE = 'dev';
++function setRelease(id) { if (id) RELEASE = String(id); }
++
// The OpenVibe Frame (navbar, footer, "shipped" views, themes) comes from openvibe.network; its init is
// /frame-init.js (same origin, no inline script), reading the JSON config in #ov-frame-config.
const NETWORK = 'https://openvibe.network';
@@@ -156,6 +161,8 @@ ${jsonLd ? jsonLd + '\n' : ''}${require('openvibe-shared/app-icon').headTags({ s
<script src="${ovServe.url('navbar.js')}" defer></script>
<script src="${ovServe.url('footer.js')}" defer></script>
<script src="/frame-init.js" defer></script>
++<meta name="ov-boost" content="search@${esc(RELEASE)}">
++<script src="${ovServe.url('boost.js')}" data-main="#main" defer></script>
<style>
/* The network theme (the Frame's theme loader sets these tokens on <html>) wins; the values here are the
defaults when it does not load. Search's own names follow them, so the page and the shared widgets
@@@ -187,7 +194,7 @@ mark { background: var(--mark); color: inherit; }
<body>
<div id="navbar-mount"></div>
${frame.noscriptNav({ name: 'OpenVibe.Search', links: [{ label: 'Search', href: '/' }, { label: 'Updates', href: '/updates' }] })}
--<main>
++<main id="main">
<h1><a href="/" style="color:inherit;text-decoration:none">OpenVibe.Search</a></h1>
<p class="lede">Search what the OpenVibe network's services have published. Alpha: the index holds only what they have sent so far.</p>
<form method="get" action="/" role="search">
@@@ -310,4 +317,4 @@ function pageRouter({ searcher, auth, baseUrl = 'https://search.openvibe.network
return router;
}
--module.exports = { pageRouter, TEXT_INDEX, robotsTxt, llmsTxt, sitemapXml, homeJsonLd };
++module.exports = { pageRouter, setRelease, TEXT_INDEX, robotsTxt, llmsTxt, sitemapXml, homeJsonLd };
ddiff --git a/test/page.test.js b/test/page.test.js
iindex 6e97ee7..068e840 100644
---- a/test/page.test.js
++++ b/test/page.test.js
@@@ -56,6 +56,14 @@ t('the front page is a search form, indexable, no-store, with a strict CSP', asy
assert.ok(ld[0].url.endsWith('/') && ld[1].url.endsWith('/'));
assert.ok(r.text.includes('<div id="navbar-mount"></div>') && r.text.includes('id="ov-footer"'), 'the OpenVibe Frame');
assert.ok(r.text.includes('data-ov-shipped="latest" data-service="search" href="/updates"'), 'what shipped');
++ // Boost (openvibe-shared 2.2.0): every page carries the release marker and the swap script, and <main id="main">
++ // is the element that changes, so a same-release move happens in place (any doubt is a normal load).
++ assert.match(r.text, /<meta name="ov-boost" content="search@[^"]+">/);
++ assert.match(r.text, /<script src="\/shared\/boost\.js\?v=[^"]+" data-main="#main" defer><\/script>/);
++ assert.ok(r.text.includes('<main id="main">'), 'the changing part');
++ // Sign-in must not bake the current path in: a {path} template returns to whatever page is showing.
++ const frameCfg = JSON.parse(/<script type="application\/json" id="ov-frame-config">([\s\S]*?)<\/script>/.exec(r.text)[1]);
++ assert.ok(!frameCfg.navbar.loginUrl || frameCfg.navbar.loginUrl.includes('{path}'), 'sign-in follows the current page');
});
t('the Frame init is a same-origin script and /updates is the shared log', async () => {
@@@ -67,6 +75,7 @@ t('the Frame init is a same-origin script and /updates is the shared log', async
assert.strictEqual(r.status, 200);
assert.ok(r.text.includes('What shipped on OpenVibe.Search') && r.text.includes('data-ov-shipped="log" data-service="search"'));
assert.match(r.text, /<link rel="canonical" href="https?:\/\/[^"/]+\/updates">/);
++ assert.match(r.text, /<meta name="ov-boost" content="search@[^"]+">/);
assert.match(r.headers.get('content-security-policy'), /default-src 'none'/);
});