openvibers/openvibe.news Commits
News 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.test.js pins the marker, the boost tag and the login template.
Changes
@@@ -226,7 +226,7 @@ under [Grants the Network must hold](#grants-the-network-must-hold).`openvibe-sdk/db`; Valkey holds the per-actor limit counters (optional: without `VALKEY_URL` they count per process).- **Packages** (pinned by release tarball): `openvibe-publishing` v1.0.0 (async PostgreSQL stores: revisions,citations, authorship, seo, index-hooks, ssr, taxonomy slugify), `openvibe-contracts` v0.79.0,-- `openvibe-shared` v2.0.0 (Frame, app icon, footer, legal, release, metrics, ready, seo),++ `openvibe-shared` v2.2.0 (Frame, app icon, footer, legal, release, metrics, ready, seo),`openvibe-sdk` v0.25.0 (db with after-commit hooks, PostgreSQL events outbox and inbox, webhook signatures v2,service tokens, Network sign-in (`openvibe-sdk/sso`) and the service outbox (`openvibe-sdk/events`), per-actor limits, testing).- **OpenVibe.Sources** (4720): `sources.item.read`; optionally `sources.source.read` (outlet@@@ -11,7 +11,7 @@"packages": ["openvibe-publishing v1.0.0","openvibe-contracts v0.79.0",-- "openvibe-shared v2.0.0",++ "openvibe-shared v2.2.0","openvibe-sdk v0.25.0"],"capabilities": [@@@ -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"@@@ -32,7 +32,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": {@@@ -97,6 +97,7 @@ async function createApp(opts = {}) {app.disable('x-powered-by');app.set('trust proxy', config.trustProxy);const release = require('openvibe-shared/release').createRelease({ service: 'news', root: path.join(__dirname, '..') });++ require('./render/layout').setRelease(release.release);const metrics = require('openvibe-shared/metrics').instrument(app, { service: 'news', release: release.release });app.locals.metrics = metrics.registry;app.locals.ctx = ctx;@@@ -31,6 +31,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); }++/*** o: title, description, decision (required), canonical, type ('website'|'article'), image,* jsonLd [], feeds [{ type, href, title }], body (HTML), viewer, config, csrf,@@@ -64,7 +69,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 toonotificationsRealtime: true, // the bell hears new notifications over OpenVibe.Events (Shared 1.22.0)};@@@ -89,6 +94,8 @@ ${appIcon.headTags({ site: 'news' })}<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="news@${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>@@@ -111,4 +118,4 @@ document.addEventListener('DOMContentLoaded', function () {</html>`;}--module.exports = { renderPage, asset, assetVersion, SITE_NAME, NETWORK_URL };++module.exports = { renderPage, asset, assetVersion, setRelease, SITE_NAME, NETWORK_URL };nnew file mode 100644@@@ -0,0 +1,31 @@++'use strict';++/**++ * The page shell (server/render/layout.js): every rendered page carries the boost marker (its content++ * is the release the page was built from) and the shared boost script tag pointed at <main id="main">,++ * so the site moves between its pages without a reload (openvibe-shared/boost, plan T11). 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 boost marker and the boost script tag with data-main="#main"', async () => {++ for (const path of ['/', '/topics', '/updates']) {++ const r = await t.get(path);++ assert.strictEqual(r.status, 200, `${path} → ${r.status}`);++ assert.ok(/<meta name="ov-boost" content="news@[^"]+">/.test(r.text), `${path}: boost marker with the release`);++ assert.ok(/<script src="\/shared\/boost\.js\?v=[0-9a-f]{12}" data-main="#main" defer><\/script>/.test(r.text), `${path}: boost script tag`);++ assert.ok(r.text.includes('<main id="main"'), `${path}: the swappable <main id="main">`);++ }++ });++++ await check('the shared navbar signs in through the {path} template (sign-in returns to the current page)', async () => {++ const home = await t.get('/');++ assert.ok(home.text.includes('"loginUrl":"/auth/login?next={path}"'), 'loginUrl is a {path} template');++ });++++ await t.close();++ done();++})();
@@ -226,7 +226,7 @@ under [Grants the Network must hold](#grants-the-network-must-hold).
@@ -226,7 +226,7 @@ under [Grants the Network must hold](#grants-the-network-must-hold).
`openvibe-sdk/db`; Valkey holds the per-actor limit counters (optional: without `VALKEY_URL` they count per process).
`openvibe-sdk/db`; Valkey holds the per-actor limit counters (optional: without `VALKEY_URL` they count per process).
- **Packages** (pinned by release tarball): `openvibe-publishing` v1.0.0 (async PostgreSQL stores: revisions,
- **Packages** (pinned by release tarball): `openvibe-publishing` v1.0.0 (async PostgreSQL stores: revisions,
citations, authorship, seo, index-hooks, ssr, taxonomy slugify), `openvibe-contracts` v0.79.0,
citations, authorship, seo, index-hooks, ssr, taxonomy slugify), `openvibe-contracts` v0.79.0,
- `openvibe-shared` v2.0.0 (Frame, app icon, footer, legal, release, metrics, ready, seo),
+ `openvibe-shared` v2.2.0 (Frame, app icon, footer, legal, release, metrics, ready, seo),
`openvibe-sdk` v0.25.0 (db with after-commit hooks, PostgreSQL events outbox and inbox, webhook signatures v2,
`openvibe-sdk` v0.25.0 (db with after-commit hooks, PostgreSQL events outbox and inbox, webhook signatures v2,
service tokens, Network sign-in (`openvibe-sdk/sso`) and the service outbox (`openvibe-sdk/events`), per-actor limits, testing).
service tokens, Network sign-in (`openvibe-sdk/sso`) and the service outbox (`openvibe-sdk/events`), per-actor limits, testing).
- **OpenVibe.Sources** (4720): `sources.item.read`; optionally `sources.source.read` (outlet
- **OpenVibe.Sources** (4720): `sources.item.read`; optionally `sources.source.read` (outlet
@@ -11,7 +11,7 @@
@@ -11,7 +11,7 @@
"packages": [
"packages": [
"openvibe-publishing v1.0.0",
"openvibe-publishing v1.0.0",
"openvibe-contracts v0.79.0",
"openvibe-contracts v0.79.0",
- "openvibe-shared v2.0.0",
+ "openvibe-shared v2.2.0",
"openvibe-sdk v0.25.0"
"openvibe-sdk v0.25.0"
],
],
"capabilities": [
"capabilities": [
@@ -18,7 +18,7 @@
@@ -18,7 +18,7 @@
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
"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-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-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"
"pg": "^8.23.0"
},
},
"devDependencies": {
"devDependencies": {
@@ -1210,9 +1210,9 @@
@@ -1210,9 +1210,9 @@
}
}
},
},
"node_modules/openvibe-shared": {
"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",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -32,7 +32,7 @@
@@ -32,7 +32,7 @@
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
"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-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-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"
"pg": "^8.23.0"
},
},
"devDependencies": {
"devDependencies": {
@@ -97,6 +97,7 @@ async function createApp(opts = {}) {
@@ -97,6 +97,7 @@ async function createApp(opts = {}) {
app.disable('x-powered-by');
app.disable('x-powered-by');
app.set('trust proxy', config.trustProxy);
app.set('trust proxy', config.trustProxy);
const release = require('openvibe-shared/release').createRelease({ service: 'news', root: path.join(__dirname, '..') });
const release = require('openvibe-shared/release').createRelease({ service: 'news', root: path.join(__dirname, '..') });
+ require('./render/layout').setRelease(release.release);
const metrics = require('openvibe-shared/metrics').instrument(app, { service: 'news', release: release.release });
const metrics = require('openvibe-shared/metrics').instrument(app, { service: 'news', release: release.release });
app.locals.metrics = metrics.registry;
app.locals.metrics = metrics.registry;
app.locals.ctx = ctx;
app.locals.ctx = ctx;
@@ -31,6 +31,11 @@ function assetVersion(rel) {
@@ -31,6 +31,11 @@ function assetVersion(rel) {
}
}
const asset = (rel) => `/${rel}?v=${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); }
+
/**
/**
* o: title, description, decision (required), canonical, type ('website'|'article'), image,
* o: title, description, decision (required), canonical, type ('website'|'article'), image,
* jsonLd [], feeds [{ type, href, title }], body (HTML), viewer, config, csrf,
* jsonLd [], feeds [{ type, href, title }], body (HTML), viewer, config, csrf,
@@ -64,7 +69,7 @@ function renderPage(o) {
@@ -64,7 +69,7 @@ function renderPage(o) {
history: { type: 'page', title: o.title || SITE_NAME },
history: { type: 'page', title: o.title || SITE_NAME },
silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,
silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,
sessionUrl: '/auth/me',
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
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)
notificationsRealtime: true, // the bell hears new notifications over OpenVibe.Events (Shared 1.22.0)
};
};
@@ -89,6 +94,8 @@ ${appIcon.headTags({ site: 'news' })}
@@ -89,6 +94,8 @@ ${appIcon.headTags({ site: 'news' })}
<script src="${ovServe.url('theme-loader.js')}" defer></script>
<script src="${ovServe.url('theme-loader.js')}" defer></script>
<script src="${ovServe.url('navbar.js')}" defer></script>
<script src="${ovServe.url('navbar.js')}" defer></script>
<script src="${ovServe.url('footer.js')}" defer></script>
<script src="${ovServe.url('footer.js')}" defer></script>
+<meta name="ov-boost" content="news@${esc(RELEASE)}">
+<script src="${ovServe.url('boost.js')}" data-main="#main" defer></script>
</head>
</head>
<body class="${esc(o.bodyClass || '')}">
<body class="${esc(o.bodyClass || '')}">
<a class="skip" href="#main">Skip to content</a>
<a class="skip" href="#main">Skip to content</a>
@@ -111,4 +118,4 @@ document.addEventListener('DOMContentLoaded', function () {
@@ -111,4 +118,4 @@ document.addEventListener('DOMContentLoaded', function () {
</html>`;
</html>`;
}
}
-module.exports = { renderPage, asset, assetVersion, SITE_NAME, NETWORK_URL };
+module.exports = { renderPage, asset, assetVersion, setRelease, SITE_NAME, NETWORK_URL };
new file mode 100644
new file mode 100644
@@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
+'use strict';
+/**
+ * The page shell (server/render/layout.js): every rendered page carries the boost marker (its content
+ * is the release the page was built from) and the shared boost script tag pointed at <main id="main">,
+ * so the site moves between its pages without a reload (openvibe-shared/boost, plan T11). 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 boost marker and the boost script tag with data-main="#main"', async () => {
+ for (const path of ['/', '/topics', '/updates']) {
+ const r = await t.get(path);
+ assert.strictEqual(r.status, 200, `${path} → ${r.status}`);
+ assert.ok(/<meta name="ov-boost" content="news@[^"]+">/.test(r.text), `${path}: boost marker with the release`);
+ assert.ok(/<script src="\/shared\/boost\.js\?v=[0-9a-f]{12}" data-main="#main" defer><\/script>/.test(r.text), `${path}: boost script tag`);
+ assert.ok(r.text.includes('<main id="main"'), `${path}: the swappable <main id="main">`);
+ }
+ });
+
+ await check('the shared navbar signs in through the {path} template (sign-in returns to the current page)', async () => {
+ const home = await t.get('/');
+ assert.ok(home.text.includes('"loginUrl":"/auth/login?next={path}"'), 'loginUrl is a {path} template');
+ });
+
+ await t.close();
+ done();
+})();