openvibers/openvibe.shared Commits
Legal: public and service pages carry the day they were written (#275)
Changes
@@@ -28,6 +28,9 @@// ═══════════════════════════════════════════════════════════════const UPDATED = '2026-09-18';const UPDATED_TEXT = 'September 18, 2026';++// A profile written later carries the day it was written, so its pages never claim an older date (11-03).++const PROFILE_UPDATED = { public: ['2026-11-03', 'November 3, 2026'], service: ['2026-11-03', 'November 3, 2026'] };++const updatedOf = (profile) => PROFILE_UPDATED[profile] || [UPDATED, UPDATED_TEXT];const CONTACT = '[email protected]';const PATHS = ['/terms', '/privacy', '/dmca'];const TITLES = { terms: 'Terms of Service', privacy: 'Privacy Policy', dmca: 'DMCA & Copyright' };@@@ -175,14 +178,14 @@ function build(kind, site) {const s = Object.assign({ profile: 'info' }, site);return { kind, title: `${TITLES[kind]} — ${s.name}`, heading: TITLES[kind],description: { terms: `The rules for using ${s.name}, in plain language.`, privacy: `What ${s.name} collects, why, and the choices you have.`, dmca: `How to report copyright infringement on ${s.name}, and how to respond to a report.` }[kind],-- updated: UPDATED, sections: BUILDERS[kind](s) };++ updated: updatedOf(s.profile)[0], updatedText: updatedOf(s.profile)[1], sections: BUILDERS[kind](s) };}/** Article markup only, for sites that wrap it in their own layout. */function article(kind, site) {const d = build(kind, site);const others = Object.keys(TITLES).filter(k => k !== kind);-- return `<article class="ov-legal"><p class="ov-legal-kicker">${esc(site.name)}</p><h1>${esc(d.heading)}</h1><p class="ov-legal-date">Last updated <time datetime="${d.updated}">${UPDATED_TEXT}</time></p>++ return `<article class="ov-legal"><p class="ov-legal-kicker">${esc(site.name)}</p><h1>${esc(d.heading)}</h1><p class="ov-legal-date">Last updated <time datetime="${d.updated}">${d.updatedText}</time></p><nav class="ov-legal-toc" aria-label="On this page"><ol>${d.sections.map(([h], i) => `<li><a href="#s${i + 1}">${esc(h)}</a></li>`).join('')}</ol></nav>${d.sections.map(([h, html], i) => `<section><h2 id="s${i + 1}">${esc(h)}</h2>${html}</section>`).join('\n')}<nav class="ov-legal-more" aria-label="Other policies">${others.map(k => `<a href="/${k}">${esc(TITLES[k])}</a>`).join('')}<a href="https://openvibe.network/">About OpenVibe</a></nav></article>`;@@@ -54,6 +54,10 @@ const text = (kind, site) => legal.build(kind, site).sections.map(([h, html]) =>assert.doesNotMatch(text('terms', { host: 'openvibe.live', name: 'OpenVibe.Live', profile: 'streaming' }), /What you make here|No account needed/);}++// Each profile's pages carry the day its words were written.++assert.strictEqual(legal.build('terms', { host: 'openvibe.report', name: 'R', profile: 'public' }).updated, '2026-11-03');++assert.match(legal.article('privacy', { host: 'openvibe.watch', name: 'W', profile: 'service' }), /<time datetime="2026-11-03">November 3, 2026<\/time>/);++assert.match(legal.article('terms', { host: 'openvibe.live', name: 'L', profile: 'streaming' }), /<time datetime="2026-09-18">September 18, 2026<\/time>/);assert.deepStrictEqual(legal.PROFILES, ['streaming', 'tools', 'ugc', 'games', 'hosting', 'account', 'info', 'public', 'service']);for (const profile of legal.PROFILES) for (const kind of Object.keys(legal.TITLES)) assert.ok(legal.page(kind, { host: 'a.openvibe.network', name: 'A', profile, id: 'network' }).includes('</html>'));console.log('legal profiles: all checks passed');
@@ -28,6 +28,9 @@
@@ -28,6 +28,9 @@
// ═══════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════
const UPDATED = '2026-09-18';
const UPDATED = '2026-09-18';
const UPDATED_TEXT = 'September 18, 2026';
const UPDATED_TEXT = 'September 18, 2026';
+// A profile written later carries the day it was written, so its pages never claim an older date (11-03).
+const PROFILE_UPDATED = { public: ['2026-11-03', 'November 3, 2026'], service: ['2026-11-03', 'November 3, 2026'] };
+const updatedOf = (profile) => PROFILE_UPDATED[profile] || [UPDATED, UPDATED_TEXT];
const CONTACT = '[email protected]';
const CONTACT = '[email protected]';
const PATHS = ['/terms', '/privacy', '/dmca'];
const PATHS = ['/terms', '/privacy', '/dmca'];
const TITLES = { terms: 'Terms of Service', privacy: 'Privacy Policy', dmca: 'DMCA & Copyright' };
const TITLES = { terms: 'Terms of Service', privacy: 'Privacy Policy', dmca: 'DMCA & Copyright' };
@@ -175,14 +178,14 @@ function build(kind, site) {
@@ -175,14 +178,14 @@ function build(kind, site) {
const s = Object.assign({ profile: 'info' }, site);
const s = Object.assign({ profile: 'info' }, site);
return { kind, title: `${TITLES[kind]} — ${s.name}`, heading: TITLES[kind],
return { kind, title: `${TITLES[kind]} — ${s.name}`, heading: TITLES[kind],
description: { terms: `The rules for using ${s.name}, in plain language.`, privacy: `What ${s.name} collects, why, and the choices you have.`, dmca: `How to report copyright infringement on ${s.name}, and how to respond to a report.` }[kind],
description: { terms: `The rules for using ${s.name}, in plain language.`, privacy: `What ${s.name} collects, why, and the choices you have.`, dmca: `How to report copyright infringement on ${s.name}, and how to respond to a report.` }[kind],
- updated: UPDATED, sections: BUILDERS[kind](s) };
+ updated: updatedOf(s.profile)[0], updatedText: updatedOf(s.profile)[1], sections: BUILDERS[kind](s) };
}
}
/** Article markup only, for sites that wrap it in their own layout. */
/** Article markup only, for sites that wrap it in their own layout. */
function article(kind, site) {
function article(kind, site) {
const d = build(kind, site);
const d = build(kind, site);
const others = Object.keys(TITLES).filter(k => k !== kind);
const others = Object.keys(TITLES).filter(k => k !== kind);
- return `<article class="ov-legal"><p class="ov-legal-kicker">${esc(site.name)}</p><h1>${esc(d.heading)}</h1><p class="ov-legal-date">Last updated <time datetime="${d.updated}">${UPDATED_TEXT}</time></p>
+ return `<article class="ov-legal"><p class="ov-legal-kicker">${esc(site.name)}</p><h1>${esc(d.heading)}</h1><p class="ov-legal-date">Last updated <time datetime="${d.updated}">${d.updatedText}</time></p>
<nav class="ov-legal-toc" aria-label="On this page"><ol>${d.sections.map(([h], i) => `<li><a href="#s${i + 1}">${esc(h)}</a></li>`).join('')}</ol></nav>
<nav class="ov-legal-toc" aria-label="On this page"><ol>${d.sections.map(([h], i) => `<li><a href="#s${i + 1}">${esc(h)}</a></li>`).join('')}</ol></nav>
${d.sections.map(([h, html], i) => `<section><h2 id="s${i + 1}">${esc(h)}</h2>${html}</section>`).join('\n')}
${d.sections.map(([h, html], i) => `<section><h2 id="s${i + 1}">${esc(h)}</h2>${html}</section>`).join('\n')}
<nav class="ov-legal-more" aria-label="Other policies">${others.map(k => `<a href="/${k}">${esc(TITLES[k])}</a>`).join('')}<a href="https://openvibe.network/">About OpenVibe</a></nav></article>`;
<nav class="ov-legal-more" aria-label="Other policies">${others.map(k => `<a href="/${k}">${esc(TITLES[k])}</a>`).join('')}<a href="https://openvibe.network/">About OpenVibe</a></nav></article>`;
@@ -54,6 +54,10 @@ const text = (kind, site) => legal.build(kind, site).sections.map(([h, html]) =>
@@ -54,6 +54,10 @@ const text = (kind, site) => legal.build(kind, site).sections.map(([h, html]) =>
assert.doesNotMatch(text('terms', { host: 'openvibe.live', name: 'OpenVibe.Live', profile: 'streaming' }), /What you make here|No account needed/);
assert.doesNotMatch(text('terms', { host: 'openvibe.live', name: 'OpenVibe.Live', profile: 'streaming' }), /What you make here|No account needed/);
}
}
+// Each profile's pages carry the day its words were written.
+assert.strictEqual(legal.build('terms', { host: 'openvibe.report', name: 'R', profile: 'public' }).updated, '2026-11-03');
+assert.match(legal.article('privacy', { host: 'openvibe.watch', name: 'W', profile: 'service' }), /<time datetime="2026-11-03">November 3, 2026<\/time>/);
+assert.match(legal.article('terms', { host: 'openvibe.live', name: 'L', profile: 'streaming' }), /<time datetime="2026-09-18">September 18, 2026<\/time>/);
assert.deepStrictEqual(legal.PROFILES, ['streaming', 'tools', 'ugc', 'games', 'hosting', 'account', 'info', 'public', 'service']);
assert.deepStrictEqual(legal.PROFILES, ['streaming', 'tools', 'ugc', 'games', 'hosting', 'account', 'info', 'public', 'service']);
for (const profile of legal.PROFILES) for (const kind of Object.keys(legal.TITLES)) assert.ok(legal.page(kind, { host: 'a.openvibe.network', name: 'A', profile, id: 'network' }).includes('</html>'));
for (const profile of legal.PROFILES) for (const kind of Object.keys(legal.TITLES)) assert.ok(legal.page(kind, { host: 'a.openvibe.network', name: 'A', profile, id: 'network' }).includes('</html>'));
console.log('legal profiles: all checks passed');
console.log('legal profiles: all checks passed');