openvibers/openvibe.vip Commits
Render every VIP page through openvibe-shared/shell v2.6.0
Changes
@@@ -16,7 +16,7 @@"jsonwebtoken": "^9.0.2","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.26.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.5.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.6.0","pg": "^8.23.0"},"devDependencies": {@@@ -1162,9 +1162,9 @@}},"node_modules/openvibe-shared": {-- "version": "2.5.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.5.0",-- "integrity": "sha512-fHuUS6SXM7ezatfAyqLm1V8B6puV9YXSnz6aG6XQbWx28qghRCCHeUF3lr4nxiCGdn9SyikYtIyLGL5r1ycp9Q==",++ "version": "2.6.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.6.0",++ "integrity": "sha512-IKKvWTM/9UonQ4msrzM4yir0QcTUNbhyqeN1r5ywWavU4VTsL1sI5QdHQlxge7BAsMrQ4OvMEPlHjuAfUJM3rg==","license": "MIT","dependencies": {"jsonwebtoken": "^9.0.0"@@@ -33,7 +33,7 @@"jsonwebtoken": "^9.0.2","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.26.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.5.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.6.0","pg": "^8.23.0"},"engines": {@@@ -1,10 +1,10 @@'use strict';/**-- * Page shell for every server-rendered page: full <head> SEO (title, description, canonical,-- * robots, Open Graph), the shared OpenVibe Frame (inline critical canvas + app icon from-- * openvibe-shared, navbar.js from the Network, the SSR footer and a <noscript> navigation), this-- * site's stylesheet and its small progressive script. Everything is useful without JavaScript.++ * Page shell for every server-rendered page, composed by openvibe-shared/shell: full <head> SEO++ * (title, description, canonical, robots, Open Graph, JSON-LD), the shared OpenVibe Frame (app icon,++ * navbar.js from the Network, the SSR footer and a <noscript> navigation), this site's stylesheet++ * and its small progressive script. Everything is useful without JavaScript.*/const crypto = require('crypto');const ovServe = require('openvibe-shared/serve');@@@ -12,6 +12,7 @@ const fs = require('fs');const path = require('path');const appIcon = require('openvibe-shared/app-icon');const frame = require('openvibe-shared/frame');++const shell = require('openvibe-shared/shell');const SITE_NAME = 'OpenVibe.VIP';const NETWORK_URL = 'https://openvibe.network';@@@ -49,10 +50,8 @@ function createLayout({ config, release }) {* active ('home'|'memberships'|'dashboard'|'creator'), ogImage, jsonLd (array), viewer*/function page(o) {-- const title = o.title ? `${o.title} · ${SITE_NAME}` : `${SITE_NAME} — memberships across OpenVibe`;const description = (o.description || DEFAULT_DESCRIPTION).replace(/\s+/g, ' ').trim().slice(0, 300);const canonical = abs(o.canonicalPath || '/');-- const robots = o.robots || 'index,follow';const nav = {service: 'vip', apiBase: NETWORK_URL,links: NAV_LINKS.map((l) => ({ ...l, active: o.active === l.label.toLowerCase() })),@@@ -64,56 +63,44 @@ function createLayout({ config, release }) {notificationsRealtime: true, // the bell hears new notifications over OpenVibe.Events (Shared 1.22.0)};const footer = { service: 'vip', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };-- const jsonLd = (o.jsonLd || []).map((x) => `<script type="application/ld+json">${JSON.stringify(x).replace(/</g, '\\u003c')}</script>`).join('\n');const who = o.viewer? `<span class="who">Signed in as <b>${esc(o.viewer.name || o.viewer.username || 'you')}</b> · <a href="/auth/logout?next=/">Sign out</a></span>`: `<a class="who" href="/auth/login?next=${encodeURIComponent(o.canonicalPath || '/')}">Sign in</a>`;-- return `<!DOCTYPE html>--<html lang="en" data-page="${esc(o.active || 'page')}">--<head>--<meta charset="utf-8">--<meta name="viewport" content="width=device-width, initial-scale=1">--<title>${esc(title)}</title>--<meta name="description" content="${esc(description)}">--<link rel="canonical" href="${esc(canonical)}">--<meta name="robots" content="${esc(robots)}">--<meta name="referrer" content="strict-origin-when-cross-origin">--<meta property="og:site_name" content="${SITE_NAME}">--<meta property="og:type" content="${o.ogType || 'website'}">--<meta property="og:title" content="${esc(o.title || SITE_NAME)}">--<meta property="og:description" content="${esc(description)}">--<meta property="og:url" content="${esc(canonical)}">--${o.ogImage ? `<meta property="og:image" content="${esc(o.ogImage)}">` : ''}--<meta name="twitter:card" content="summary">--${appIcon.headTags({ site: 'vip' })}--${release ? release.metaTag() : ''}--<script src="${ovServe.url('theme-loader.js')}" defer></script>--<link rel="stylesheet" href="${asset('css/vip.css')}">--${jsonLd}--<script src="${ovServe.url('navbar.js')}" defer></script>--<script src="${ovServe.url('footer.js')}" defer></script>--<script src="${asset('js/vip.js')}" defer></script>--<meta name="ov-boost" content="vip@${esc(RELEASE)}">--<script src="${ovServe.url('boost.js')}" data-main="#main" defer></script>--</head>--<body>--<div id="navbar-mount"></div>--${frame.noscriptNav({ name: SITE_NAME, home: '/', links: NAV_LINKS })}++ // shell.page writes the document, the SEO head (+ the WebPage JSON-LD and ai-summary from the++ // summary), the theme-loader, navbar.js/footer.js with the navbar init, the noscript nav and++ // the SSR footer; the rest of the head is this site's own.++ return shell.page({++ name: SITE_NAME, service: 'vip', lang: 'en',++ title: o.title || `${SITE_NAME} — memberships across OpenVibe`,++ titleSuffix: o.title ? ` · ${SITE_NAME}` : undefined,++ siteName: SITE_NAME, description, canonical, robots: o.robots || 'index,follow',++ type: o.ogType || 'website', image: o.ogImage, jsonLd: o.jsonLd,++ summary: description, url: canonical,++ navbar: nav, footer, home: '/', navLinks: NAV_LINKS,++ head: [++ '<meta name="referrer" content="strict-origin-when-cross-origin">',++ appIcon.headTags({ site: 'vip' }),++ release ? release.metaTag() : '',++ `<link rel="stylesheet" href="${asset('css/vip.css')}">`,++ `<meta name="ov-boost" content="vip@${esc(RELEASE)}">`,++ `<script src="${ovServe.url('boost.js')}" data-main="#main" defer></script>`,++ `<script src="${asset('js/vip.js')}" defer></script>`,++ ].filter(Boolean).join('\n'),++ body: `<div id="navbar-mount"></div><header class="site-head"><a class="brand" href="/">${SITE_NAME}</a><nav>${NAV_LINKS.map((l) => `<a href="${l.href}"${o.active === l.label.toLowerCase() ? ' aria-current="page"' : ''}>${l.label}</a>`).join('')}</nav>${who}</header><main id="main" class="page">${o.body || ''}${o.canonicalPath === '/' && o.active === 'home' ? frame.shipped({ service: 'vip', title: `Recently shipped on ${SITE_NAME}` }) : ''}</main>--${frame.footer(footer)}<script>window.__OV_PAGE = ${JSON.stringify({ navbar: nav, footer }).replace(/</g, '\\u003c')};--document.addEventListener('DOMContentLoaded', function () {-- try { if (window.OpenVibeNavbar) { OpenVibeNavbar.init(window.__OV_PAGE.navbar); document.documentElement.classList.add('ov-has-navbar'); } } catch (e) { /* the SSR header stays */ }-- try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* */ }++window.addEventListener('DOMContentLoaded', function () {++ if (window.OpenVibeNavbar) document.documentElement.classList.add('ov-has-navbar');++ try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* the SSR footer stays */ }});--</script>--</body>--</html>`;++</script>`,++ bodyAttributes: { 'data-page': o.active || 'page' },++ });}return { page, abs };@@@ -183,6 +183,32 @@ const { test, run } = harness('api');assert.match(sm.text, /http:\/\/vip\.test\/lena/);});++ test('the head comes from openvibe-shared/shell and keeps every tag and script of the site', async () => {++ const p = await t.page('/lena');++ const head = p.text.slice(0, p.text.indexOf('</head>'));++ assert.strictEqual(head.match(/<title>/g).length, 1, 'exactly one <title>');++ assert.match(head, /<title>Lena memberships · OpenVibe\.VIP<\/title>/);++ assert.match(head, /<link rel="canonical" href="http:\/\/vip\.test\/lena">/);++ assert.match(head, /<meta name="robots" content="index,follow">/);++ assert.match(head, /<meta property="og:title" content="Lena memberships · OpenVibe\.VIP">/);++ assert.match(head, /<meta name="ai-summary" content="[^"]+">/);++ assert.match(head, /<script type="application\/ld\+json">\{[^<]*"@type":"WebPage"/);++ assert.match(head, /<meta name="referrer" content="strict-origin-when-cross-origin">/);++ assert.match(head, /<link rel="icon"[^>]*data-ov-icon="vip"/, 'the app-icon tags');++ assert.match(head, /<link rel="stylesheet" href="\/css\/vip\.css\?v=[^"]+">/);++ assert.match(head, /<meta name="ov-boost" content="vip@[^"]+">/);++ assert.match(head, /<script src="\/shared\/boost\.js\?v=[0-9a-f]{12}" data-main="#main" defer><\/script>/);++ assert.match(head, /<script src="\/js\/vip\.js\?v=[^"]+" defer><\/script>/);++ for (const s of ['theme-loader', 'navbar', 'footer']) assert.match(head, new RegExp(`/shared/${s}\\.js\\?v=[0-9a-f]{12}`), s);++ assert.match(p.text, /<body data-page="creator">/);++ assert.match(p.text, /<header class="site-head">/);++ assert.match(p.text, /<main id="main" class="page">/);++ assert.match(p.text, /id="ov-footer"/);++ assert.match(p.text, /OpenVibeFooter\.init\(window\.__OV_PAGE\.footer\)/);++ const home = await t.page('/');++ assert.match(home.text, /<title>OpenVibe\.VIP — memberships across OpenVibe<\/title>/);++ });++test('discovery artifacts: robots.txt, sitemap.xml and llms.txt from the shared seo kit', async () => {const robots = await t.call('GET', '/robots.txt', { token: null });assert.strictEqual(robots.status, 200);
@@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@
"jsonwebtoken": "^9.0.2",
"jsonwebtoken": "^9.0.2",
"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-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.26.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.26.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.5.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.6.0",
"pg": "^8.23.0"
"pg": "^8.23.0"
},
},
"devDependencies": {
"devDependencies": {
@@ -1162,9 +1162,9 @@
@@ -1162,9 +1162,9 @@
}
}
},
},
"node_modules/openvibe-shared": {
"node_modules/openvibe-shared": {
- "version": "2.5.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.5.0",
- "integrity": "sha512-fHuUS6SXM7ezatfAyqLm1V8B6puV9YXSnz6aG6XQbWx28qghRCCHeUF3lr4nxiCGdn9SyikYtIyLGL5r1ycp9Q==",
+ "version": "2.6.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.6.0",
+ "integrity": "sha512-IKKvWTM/9UonQ4msrzM4yir0QcTUNbhyqeN1r5ywWavU4VTsL1sI5QdHQlxge7BAsMrQ4OvMEPlHjuAfUJM3rg==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -33,7 +33,7 @@
@@ -33,7 +33,7 @@
"jsonwebtoken": "^9.0.2",
"jsonwebtoken": "^9.0.2",
"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-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.26.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.26.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.5.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.6.0",
"pg": "^8.23.0"
"pg": "^8.23.0"
},
},
"engines": {
"engines": {
@@ -1,10 +1,10 @@
@@ -1,10 +1,10 @@
'use strict';
'use strict';
/**
/**
- * Page shell for every server-rendered page: full <head> SEO (title, description, canonical,
- * robots, Open Graph), the shared OpenVibe Frame (inline critical canvas + app icon from
- * openvibe-shared, navbar.js from the Network, the SSR footer and a <noscript> navigation), this
- * site's stylesheet and its small progressive script. Everything is useful without JavaScript.
+ * Page shell for every server-rendered page, composed by openvibe-shared/shell: full <head> SEO
+ * (title, description, canonical, robots, Open Graph, JSON-LD), the shared OpenVibe Frame (app icon,
+ * navbar.js from the Network, the SSR footer and a <noscript> navigation), this site's stylesheet
+ * and its small progressive script. Everything is useful without JavaScript.
*/
*/
const crypto = require('crypto');
const crypto = require('crypto');
const ovServe = require('openvibe-shared/serve');
const ovServe = require('openvibe-shared/serve');
@@ -12,6 +12,7 @@ const fs = require('fs');
@@ -12,6 +12,7 @@ const fs = require('fs');
const path = require('path');
const path = require('path');
const appIcon = require('openvibe-shared/app-icon');
const appIcon = require('openvibe-shared/app-icon');
const frame = require('openvibe-shared/frame');
const frame = require('openvibe-shared/frame');
+const shell = require('openvibe-shared/shell');
const SITE_NAME = 'OpenVibe.VIP';
const SITE_NAME = 'OpenVibe.VIP';
const NETWORK_URL = 'https://openvibe.network';
const NETWORK_URL = 'https://openvibe.network';
@@ -49,10 +50,8 @@ function createLayout({ config, release }) {
@@ -49,10 +50,8 @@ function createLayout({ config, release }) {
* active ('home'|'memberships'|'dashboard'|'creator'), ogImage, jsonLd (array), viewer
* active ('home'|'memberships'|'dashboard'|'creator'), ogImage, jsonLd (array), viewer
*/
*/
function page(o) {
function page(o) {
- const title = o.title ? `${o.title} · ${SITE_NAME}` : `${SITE_NAME} — memberships across OpenVibe`;
const description = (o.description || DEFAULT_DESCRIPTION).replace(/\s+/g, ' ').trim().slice(0, 300);
const description = (o.description || DEFAULT_DESCRIPTION).replace(/\s+/g, ' ').trim().slice(0, 300);
const canonical = abs(o.canonicalPath || '/');
const canonical = abs(o.canonicalPath || '/');
- const robots = o.robots || 'index,follow';
const nav = {
const nav = {
service: 'vip', apiBase: NETWORK_URL,
service: 'vip', apiBase: NETWORK_URL,
links: NAV_LINKS.map((l) => ({ ...l, active: o.active === l.label.toLowerCase() })),
links: NAV_LINKS.map((l) => ({ ...l, active: o.active === l.label.toLowerCase() })),
@@ -64,56 +63,44 @@ function createLayout({ config, release }) {
@@ -64,56 +63,44 @@ function createLayout({ config, release }) {
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)
};
};
const footer = { service: 'vip', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };
const footer = { service: 'vip', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };
- const jsonLd = (o.jsonLd || []).map((x) => `<script type="application/ld+json">${JSON.stringify(x).replace(/</g, '\\u003c')}</script>`).join('\n');
const who = o.viewer
const who = o.viewer
? `<span class="who">Signed in as <b>${esc(o.viewer.name || o.viewer.username || 'you')}</b> · <a href="/auth/logout?next=/">Sign out</a></span>`
? `<span class="who">Signed in as <b>${esc(o.viewer.name || o.viewer.username || 'you')}</b> · <a href="/auth/logout?next=/">Sign out</a></span>`
: `<a class="who" href="/auth/login?next=${encodeURIComponent(o.canonicalPath || '/')}">Sign in</a>`;
: `<a class="who" href="/auth/login?next=${encodeURIComponent(o.canonicalPath || '/')}">Sign in</a>`;
- return `<!DOCTYPE html>
-<html lang="en" data-page="${esc(o.active || 'page')}">
-<head>
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width, initial-scale=1">
-<title>${esc(title)}</title>
-<meta name="description" content="${esc(description)}">
-<link rel="canonical" href="${esc(canonical)}">
-<meta name="robots" content="${esc(robots)}">
-<meta name="referrer" content="strict-origin-when-cross-origin">
-<meta property="og:site_name" content="${SITE_NAME}">
-<meta property="og:type" content="${o.ogType || 'website'}">
-<meta property="og:title" content="${esc(o.title || SITE_NAME)}">
-<meta property="og:description" content="${esc(description)}">
-<meta property="og:url" content="${esc(canonical)}">
-${o.ogImage ? `<meta property="og:image" content="${esc(o.ogImage)}">` : ''}
-<meta name="twitter:card" content="summary">
-${appIcon.headTags({ site: 'vip' })}
-${release ? release.metaTag() : ''}
-<script src="${ovServe.url('theme-loader.js')}" defer></script>
-<link rel="stylesheet" href="${asset('css/vip.css')}">
-${jsonLd}
-<script src="${ovServe.url('navbar.js')}" defer></script>
-<script src="${ovServe.url('footer.js')}" defer></script>
-<script src="${asset('js/vip.js')}" defer></script>
-<meta name="ov-boost" content="vip@${esc(RELEASE)}">
-<script src="${ovServe.url('boost.js')}" data-main="#main" defer></script>
-</head>
-<body>
-<div id="navbar-mount"></div>
-${frame.noscriptNav({ name: SITE_NAME, home: '/', links: NAV_LINKS })}
+ // shell.page writes the document, the SEO head (+ the WebPage JSON-LD and ai-summary from the
+ // summary), the theme-loader, navbar.js/footer.js with the navbar init, the noscript nav and
+ // the SSR footer; the rest of the head is this site's own.
+ return shell.page({
+ name: SITE_NAME, service: 'vip', lang: 'en',
+ title: o.title || `${SITE_NAME} — memberships across OpenVibe`,
+ titleSuffix: o.title ? ` · ${SITE_NAME}` : undefined,
+ siteName: SITE_NAME, description, canonical, robots: o.robots || 'index,follow',
+ type: o.ogType || 'website', image: o.ogImage, jsonLd: o.jsonLd,
+ summary: description, url: canonical,
+ navbar: nav, footer, home: '/', navLinks: NAV_LINKS,
+ head: [
+ '<meta name="referrer" content="strict-origin-when-cross-origin">',
+ appIcon.headTags({ site: 'vip' }),
+ release ? release.metaTag() : '',
+ `<link rel="stylesheet" href="${asset('css/vip.css')}">`,
+ `<meta name="ov-boost" content="vip@${esc(RELEASE)}">`,
+ `<script src="${ovServe.url('boost.js')}" data-main="#main" defer></script>`,
+ `<script src="${asset('js/vip.js')}" defer></script>`,
+ ].filter(Boolean).join('\n'),
+ body: `<div id="navbar-mount"></div>
<header class="site-head"><a class="brand" href="/">${SITE_NAME}</a><nav>${NAV_LINKS.map((l) => `<a href="${l.href}"${o.active === l.label.toLowerCase() ? ' aria-current="page"' : ''}>${l.label}</a>`).join('')}</nav>${who}</header>
<header class="site-head"><a class="brand" href="/">${SITE_NAME}</a><nav>${NAV_LINKS.map((l) => `<a href="${l.href}"${o.active === l.label.toLowerCase() ? ' aria-current="page"' : ''}>${l.label}</a>`).join('')}</nav>${who}</header>
<main id="main" class="page">
<main id="main" class="page">
${o.body || ''}
${o.body || ''}
${o.canonicalPath === '/' && o.active === 'home' ? frame.shipped({ service: 'vip', title: `Recently shipped on ${SITE_NAME}` }) : ''}
${o.canonicalPath === '/' && o.active === 'home' ? frame.shipped({ service: 'vip', title: `Recently shipped on ${SITE_NAME}` }) : ''}
</main>
</main>
-${frame.footer(footer)}
<script>
<script>
window.__OV_PAGE = ${JSON.stringify({ navbar: nav, footer }).replace(/</g, '\\u003c')};
window.__OV_PAGE = ${JSON.stringify({ navbar: nav, footer }).replace(/</g, '\\u003c')};
-document.addEventListener('DOMContentLoaded', function () {
- try { if (window.OpenVibeNavbar) { OpenVibeNavbar.init(window.__OV_PAGE.navbar); document.documentElement.classList.add('ov-has-navbar'); } } catch (e) { /* the SSR header stays */ }
- try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* */ }
+window.addEventListener('DOMContentLoaded', function () {
+ if (window.OpenVibeNavbar) document.documentElement.classList.add('ov-has-navbar');
+ try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* the SSR footer stays */ }
});
});
-</script>
-</body>
-</html>`;
+</script>`,
+ bodyAttributes: { 'data-page': o.active || 'page' },
+ });
}
}
return { page, abs };
return { page, abs };
@@ -183,6 +183,32 @@ const { test, run } = harness('api');
@@ -183,6 +183,32 @@ const { test, run } = harness('api');
assert.match(sm.text, /http:\/\/vip\.test\/lena/);
assert.match(sm.text, /http:\/\/vip\.test\/lena/);
});
});
+ test('the head comes from openvibe-shared/shell and keeps every tag and script of the site', async () => {
+ const p = await t.page('/lena');
+ const head = p.text.slice(0, p.text.indexOf('</head>'));
+ assert.strictEqual(head.match(/<title>/g).length, 1, 'exactly one <title>');
+ assert.match(head, /<title>Lena memberships · OpenVibe\.VIP<\/title>/);
+ assert.match(head, /<link rel="canonical" href="http:\/\/vip\.test\/lena">/);
+ assert.match(head, /<meta name="robots" content="index,follow">/);
+ assert.match(head, /<meta property="og:title" content="Lena memberships · OpenVibe\.VIP">/);
+ assert.match(head, /<meta name="ai-summary" content="[^"]+">/);
+ assert.match(head, /<script type="application\/ld\+json">\{[^<]*"@type":"WebPage"/);
+ assert.match(head, /<meta name="referrer" content="strict-origin-when-cross-origin">/);
+ assert.match(head, /<link rel="icon"[^>]*data-ov-icon="vip"/, 'the app-icon tags');
+ assert.match(head, /<link rel="stylesheet" href="\/css\/vip\.css\?v=[^"]+">/);
+ assert.match(head, /<meta name="ov-boost" content="vip@[^"]+">/);
+ assert.match(head, /<script src="\/shared\/boost\.js\?v=[0-9a-f]{12}" data-main="#main" defer><\/script>/);
+ assert.match(head, /<script src="\/js\/vip\.js\?v=[^"]+" defer><\/script>/);
+ for (const s of ['theme-loader', 'navbar', 'footer']) assert.match(head, new RegExp(`/shared/${s}\\.js\\?v=[0-9a-f]{12}`), s);
+ assert.match(p.text, /<body data-page="creator">/);
+ assert.match(p.text, /<header class="site-head">/);
+ assert.match(p.text, /<main id="main" class="page">/);
+ assert.match(p.text, /id="ov-footer"/);
+ assert.match(p.text, /OpenVibeFooter\.init\(window\.__OV_PAGE\.footer\)/);
+ const home = await t.page('/');
+ assert.match(home.text, /<title>OpenVibe\.VIP — memberships across OpenVibe<\/title>/);
+ });
+
test('discovery artifacts: robots.txt, sitemap.xml and llms.txt from the shared seo kit', async () => {
test('discovery artifacts: robots.txt, sitemap.xml and llms.txt from the shared seo kit', async () => {
const robots = await t.call('GET', '/robots.txt', { token: null });
const robots = await t.call('GET', '/robots.txt', { token: null });
assert.strictEqual(robots.status, 200);
assert.strictEqual(robots.status, 200);