openvibers/openvibe.trade Commits

Trade moves between its pages without a reload (plan T11): openvibe-shared v2.2.0 boost swaps <main> styles-first with the shared transition, prefetching on intent, and does a normal load across a deploy (the ov-boost marker carries the release). The navbar's sign-in returns to the current page. test/layout-boost.test.js pins the marker, the boost tag and the login template.

OP OpenVibers committed 29 Sept 2026 · a7c11cd8bd · 7 files, +47 −9


Changes

ddiff --git a/README.md b/README.md
iindex ae1e6da..1a033d1 100644
---- a/README.md
++++ b/README.md
@@@ -211,7 +211,7 @@ services need to call Trade, is under [Grants the Network must hold](#grants-the
- **PostgreSQL 18 and Valkey 9** (OpenVibe.Host `roles/data/`, ADR-035): every read and write is async through
`openvibe-sdk/db`; Valkey holds the per-actor limit counters (optional: without `VALKEY_URL` they count per process).
- **Packages** (pinned by release tarball): `openvibe-contracts` v0.76.0, `openvibe-publishing`
-- v1.0.0 (revisions, authorship, seo, index-hooks, ssr), `openvibe-shared` v1.27.0 (Frame, app icon,
++ v1.0.0 (revisions, authorship, seo, index-hooks, ssr), `openvibe-shared` v2.2.0 (Frame, app icon,
footer, legal, release, metrics, ready), `openvibe-sdk` v0.20.0 (events outbox and inbox, webhook
signatures v2, service tokens, per-actor limits).
- **OpenVibe.Network:** SSO (OAuth client `trade`, redirect `https://openvibe.trade/auth/callback`),
ddiff --git a/STATUS.json b/STATUS.json
iindex aa45ea9..83ae0a8 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -12,7 +12,7 @@
"packages": [
"openvibe-contracts v0.79.0",
"openvibe-publishing v1.0.0",
-- "openvibe-shared v2.0.0",
++ "openvibe-shared v2.2.0",
"openvibe-sdk v0.25.0"
],
"capabilities": [
ddiff --git a/package-lock.json b/package-lock.json
iindex 4d4f41d..5261036 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -18,7 +18,7 @@
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.0.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.25.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.0.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.2.0",
"pg": "^8.23.0"
},
"devDependencies": {
@@@ -1210,9 +1210,9 @@
}
},
"node_modules/openvibe-shared": {
-- "version": "2.0.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.0.0",
-- "integrity": "sha512-n5CHC0hfxOo0jo/3kS4O3KiwhirhZDlx5zZTIuPyIf3Dq0yI/R6R64btx7NYwZe5dI0NjvqzEtnv19m8unwLMg==",
++ "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 b25afa0..ee4556f 100644
---- a/package.json
++++ b/package.json
@@@ -30,7 +30,7 @@
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.0.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.25.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.0.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.2.0",
"pg": "^8.23.0"
},
"devDependencies": {
ddiff --git a/server/app.js b/server/app.js
iindex b9f2019..fde862a 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -108,6 +108,7 @@ async function createApp(opts = {}) {
app.set('trust proxy', config.trustProxy);
// metricsPath: open tabs report their update outcomes to POST /release-metrics (defined below).
const release = require('openvibe-shared/release').createRelease({ service: 'trade', root: path.join(__dirname, '..'), metricsPath: '/release-metrics' });
++ require('./render/layout').setRelease(release.release);
const metrics = require('openvibe-shared/metrics').instrument(app, { service: 'trade', release: release.release });
app.locals.metrics = metrics.registry;
app.locals.ctx = ctx;
ddiff --git a/server/render/layout.js b/server/render/layout.js
iindex 7f1acbd..a8b2145 100644
---- a/server/render/layout.js
++++ b/server/render/layout.js
@@@ -34,6 +34,11 @@ function assetVersion(rel) {
}
const asset = (rel) => `/${rel}?v=${assetVersion(rel)}`;
++// 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); }
++
const NAV_LINKS = [
{ label: 'Instruments', href: '/' },
{ label: 'Sources', href: '/sources' },
@@@ -65,7 +70,7 @@ function renderPage(o) {
history: { type: 'page', title: o.title || SITE_NAME },
silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,
sessionUrl: '/auth/me',
-- loginUrl: `/auth/login?next=${loginNext}`,
++ loginUrl: '/auth/login?next={path}', // filled from the current page (boost moves between pages)
logoutUrl: '/auth/logout?next={path}', // Sign out in the shared navbar ends this site's session too
notificationsRealtime: true, // the bell hears new notifications over OpenVibe.Events (Shared 1.22.0)
};
@@@ -88,6 +93,8 @@ ${appIcon.headTags({ site: 'trade' })}
<script src="${ovServe.url('theme-loader.js')}" defer></script>
<script src="${ovServe.url('navbar.js')}" defer></script>
<script src="${ovServe.url('footer.js')}" defer></script>
++<meta name="ov-boost" content="trade@${esc(RELEASE)}">
++<script src="${ovServe.url('boost.js')}" data-main="#main" defer></script>
</head>
<body class="${esc(o.bodyClass || '')}">
<a class="skip" href="#main">Skip to content</a>
@@@ -112,4 +119,4 @@ document.addEventListener('DOMContentLoaded', function () {
</html>`;
}
--module.exports = { renderPage, asset, assetVersion, SITE_NAME, NETWORK_URL, DISCLAIMER };
++module.exports = { renderPage, asset, assetVersion, setRelease, SITE_NAME, NETWORK_URL, DISCLAIMER };
ddiff --git a/test/layout-boost.test.js b/test/layout-boost.test.js
nnew file mode 100644
iindex 0000000..eb24329
---- /dev/null
++++ b/test/layout-boost.test.js
@@@ -0,0 +1,30 @@
++'use strict';
++/**
++ * Page moves between pages without a reload (plan T11, openvibe-shared 2.2.0 boost): every page the layout
++ * renders carries the release marker and the boost script (data-main="#main", the element boost swaps), and
++ * the shared navbar's sign-in is a {path} template so it returns to whatever page is showing.
++ */
++const assert = require('assert');
++const { boot, check, done } = require('./helpers/boot');
++
++(async () => {
++ const t = await boot();
++
++ await check('every rendered page carries the ov-boost marker and the boost script with data-main', async () => {
++ for (const p of ['/', '/sources', '/does-not-exist']) {
++ const r = await t.get(p);
++ assert.ok(r.text.includes('<meta name="ov-boost" content="trade@'), `${p}: no ov-boost marker`);
++ assert.ok(/<script src="\/shared\/boost\.js\?v=[^"]+" data-main="#main" defer><\/script>/.test(r.text), `${p}: no boost script with data-main`);
++ assert.ok(r.text.includes('<main id="main"'), `${p}: main#main missing`);
++ }
++ });
++
++ await check('the shared navbar signs in with a {path} template (returns to the current page)', async () => {
++ const html = (await t.get('/sources')).text;
++ assert.ok(html.includes('"loginUrl":"/auth/login?next={path}"'), 'navbar loginUrl is not the {path} template');
++ assert.ok(!html.includes('"loginUrl":"/auth/login?next=%2Fsources"'), 'the shared navbar still bakes the current path');
++ });
++
++ await t.close();
++ done();
++})();