openvibers/openvibe.sdk Commits
The Frame rename, completed: the renamed files carry their new names inside (the previous commit staged them before the edit)
Changes
@@@ -1,7 +1,7 @@--// Generated by scripts/esm.js from src/chrome.js. Do not edit.--import cjs from '../src/chrome.js';++// Generated by scripts/esm.js from src/frame.js. Do not edit.++import cjs from '../src/frame.js';export const {-- mountChrome, chromeTags, chromeConfig, scriptUrls, DEFAULT_BASE,++ mountFrame, frameTags, frameConfig, scriptUrls, DEFAULT_BASE,} = cjs;export default cjs;@@@ -1,7 +1,7 @@'use strict';/**-- * openvibe-sdk/chrome — the OpenVibe network's shared chrome for any app: the universal navbar (one++ * openvibe-sdk/frame — the OpenVibe Frame: the navbar, footer and themes every OpenVibe site sits in, for any app: the universal navbar (one* account menu, notifications, the site switcher, sign in and out), the shared footer (network* links, legal links, the "shipped X ago" line and an Updates link), the "shipped" views and the* theme loader. These are the same files every OpenVibe site runs, served by OpenVibe.Network from@@@ -9,8 +9,8 @@** In the browser:*-- * // mountChrome from the openvibe-sdk/chrome entry (CommonJS or ESM)-- * const { navbar, footer } = await mountChrome({++ * // mountFrame from the openvibe-sdk/frame entry (CommonJS or ESM)++ * const { navbar, footer } = await mountFrame({* service: 'myapp', // your app's id (brand + analytics)* brand: { name: 'My App' }, // optional brand override* links: [{ label: 'Home', href: '/' }, { label: 'Docs', href: '/docs' }],@@@ -21,18 +21,18 @@* footer: { links: [{ heading: 'My App', items: [{ name: 'About', url: '/about' }] }], updates: '/updates' },* });*-- * Server-rendered apps put chromeTags(opts) in <head>/<body> instead (the same markup this module++ * Server-rendered apps put frameTags(opts) in <head>/<body> instead (the same markup this module* would inject, as a string; the JSON config is escaped for a <script> element):*-- * // chromeTags from the openvibe-sdk/chrome entry-- * const t = chromeTags({ service: 'myapp', links: [...] });++ * // frameTags from the openvibe-sdk/frame entry++ * const t = frameTags({ service: 'myapp', links: [...] });* html = `<head>${t.head}</head><body>${t.bodyStart}…${t.bodyEnd}</body>`;** Your CSP must allow `script-src https://openvibe.network` and `connect-src https://openvibe.network`* (the navbar asks /api/auth/me and the notifications there; the shipped line reads* /api/v1/changelog). Signed-in state comes from the shared ov_token, else from sessionUrl. For* your own users to be recognised across the network, sign them in with OpenVibe (openvibe-sdk/auth).-- * The chrome is progressive: if openvibe.network is unreachable your page still renders.++ * The Frame is progressive: if openvibe.network is unreachable your page still renders.*/const DEFAULT_BASE = 'https://openvibe.network';@@@ -41,7 +41,7 @@ const FILES = { theme: 'theme-loader.js', navbar: 'navbar.js', footer: 'footer.jfunction baseOf(base) {const b = String(base || DEFAULT_BASE).replace(/\/+$/, '');const ok = /^https:\/\/[a-z0-9.-]+(:\d+)?$/i.test(b) || /^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i.test(b);-- if (!ok) throw new TypeError(`openvibe-sdk/chrome: base must be an https origin (or http://localhost for development), got ${base}`);++ if (!ok) throw new TypeError(`openvibe-sdk/frame: base must be an https origin (or http://localhost for development), got ${base}`);return b;}@@@ -53,8 +53,8 @@ function scriptUrls({ base } = {}) {return out;}--/** The navbar and footer configurations mountChrome passes to OpenVibeNavbar.init / OpenVibeFooter.init. */--function chromeConfig(opts = {}) {++/** The navbar and footer configurations mountFrame passes to OpenVibeNavbar.init / OpenVibeFooter.init. */++function frameConfig(opts = {}) {const o = opts || {};const b = baseOf(o.base);const navbar = {@@@ -85,9 +85,9 @@ const jsonForScript = (v) => JSON.stringify(v).replace(/</g, '\\u003c').replace(const attr = (s) => String(s).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');/** Markup for server-rendered pages: { head, bodyStart, bodyEnd } strings. */--function chromeTags(opts = {}) {++function frameTags(opts = {}) {const urls = scriptUrls(opts);-- const cfg = chromeConfig(opts);++ const cfg = frameConfig(opts);const head = [opts.theme === false ? '' : `<script src="${attr(urls.theme)}" defer></script>`,`<script src="${attr(urls.navbar)}" defer></script>`,@@@ -103,12 +103,12 @@ function chromeTags(opts = {}) {function loadScript(doc, src) {return new Promise((resolve) => {-- const existing = doc.querySelector(`script[data-ov-chrome="${src}"]`);++ const existing = doc.querySelector(`script[data-ov-frame="${src}"]`);if (existing) { if (existing.dataset.loaded) resolve(true); else existing.addEventListener('load', () => resolve(true)); return; }const s = doc.createElement('script');s.src = src;s.async = true;-- s.setAttribute('data-ov-chrome', src);++ s.setAttribute('data-ov-frame', src);s.addEventListener('load', () => { s.dataset.loaded = '1'; resolve(true); });s.addEventListener('error', () => resolve(false));(doc.head || doc.documentElement).appendChild(s);@@@ -116,15 +116,15 @@ function loadScript(doc, src) {}/**-- * Browser: load the shared chrome and mount it. Resolves { navbar, footer } (the initialised++ * Browser: load the OpenVibe Frame and mount it. Resolves { navbar, footer } (the initialised* globals, or null for a part that could not load). Never throws for a network failure.*/--async function mountChrome(opts = {}) {++async function mountFrame(opts = {}) {const doc = (opts && opts.document) || (typeof document !== 'undefined' ? document : null);const win = (opts && opts.window) || (typeof window !== 'undefined' ? window : null);-- if (!doc || !win) throw new Error('openvibe-sdk/chrome: mountChrome runs in a browser; use chromeTags() on the server');++ if (!doc || !win) throw new Error('openvibe-sdk/frame: mountFrame runs in a browser; use frameTags() on the server');const urls = scriptUrls(opts);-- const cfg = chromeConfig(opts);++ const cfg = frameConfig(opts);if (!doc.getElementById('navbar-mount')) {const m = doc.createElement('div');m.id = 'navbar-mount';@@@ -147,4 +147,4 @@ async function mountChrome(opts = {}) {return { navbar, footer };}--module.exports = { mountChrome, chromeTags, chromeConfig, scriptUrls, DEFAULT_BASE };++module.exports = { mountFrame, frameTags, frameConfig, scriptUrls, DEFAULT_BASE };@@@ -1,8 +1,8 @@'use strict';--// openvibe-sdk/chrome: any app mounts the network's shared navbar, footer, shipped line and themes,++// openvibe-sdk/frame: any app mounts the network's shared navbar, footer, shipped line and themes,// with its own session endpoints; server-rendered apps get the same markup as strings.const assert = require('node:assert/strict');--const { mountChrome, chromeTags, chromeConfig, scriptUrls } = require('../src/chrome');++const { mountFrame, frameTags, frameConfig, scriptUrls } = require('../src/frame');const opts = {service: 'myapp',@@@ -14,15 +14,15 @@ const opts = {};// Config mapping--const cfg = chromeConfig(opts);++const cfg = frameConfig(opts);assert.equal(cfg.navbar.service, 'myapp');assert.equal(cfg.navbar.apiBase, 'https://openvibe.network');assert.equal(cfg.navbar.logoutUrl, '/auth/logout?next={path}');assert.deepEqual(cfg.navbar.menu.before[0], { label: 'My projects', href: '/projects' });assert.equal(cfg.footer.updates, '/updates');assert.equal(cfg.footer.brandName, 'My App');--assert.equal(chromeConfig({ footer: false }).footer, null);--assert.equal(chromeConfig({ shipped: false }).footer.shipped, false);++assert.equal(frameConfig({ footer: false }).footer, null);++assert.equal(frameConfig({ shipped: false }).footer.shipped, false);// URLs and base validationassert.equal(scriptUrls().navbar, 'https://openvibe.network/shared/navbar.js');@@@ -31,13 +31,13 @@ assert.throws(() => scriptUrls({ base: 'http://evil.example' }), /https origin/)assert.throws(() => scriptUrls({ base: 'javascript:alert(1)' }), /https origin/);// SSR tags: the config cannot break out of its <script>--const t = chromeTags(opts);++const t = frameTags(opts);assert.ok(t.head.includes('https://openvibe.network/shared/navbar.js') && t.head.includes('theme-loader.js') && t.head.includes('footer.js'));assert.equal(t.bodyStart, '<div id="navbar-mount"></div>');assert.ok(t.bodyEnd.includes('<footer id="ov-footer"></footer>'));assert.ok(!/<\/script><script>alert/.test(t.bodyEnd), 'a label cannot close the init script');assert.equal((t.bodyEnd.match(/<\/script>/g) || []).length, 1);--assert.ok(!chromeTags({ theme: false }).head.includes('theme-loader'));++assert.ok(!frameTags({ theme: false }).head.includes('theme-loader'));// Browser mount with a minimal DOM: scripts "load", globals appear, init receives the config.function fakeDom() {@@@ -67,7 +67,7 @@ function fakeDom() {(async () => {const d = fakeDom();-- const r = await mountChrome({ ...opts, document: d.doc, window: d.win });++ const r = await mountFrame({ ...opts, document: d.doc, window: d.win });assert.ok(r.navbar && r.footer);assert.equal(d.inits.navbar.sessionUrl, '/auth/me');assert.equal(d.inits.footer.mount, '#ov-footer');@@@ -77,8 +77,8 @@ function fakeDom() {// A failed load leaves that part null and never throws.const d2 = fakeDom();d2.doc.head.appendChild = function (c) { d2.nodes.push(c); setImmediate(() => (c.listeners.error || []).forEach((f) => f())); return c; };-- const r2 = await mountChrome({ document: d2.doc, window: d2.win });++ const r2 = await mountFrame({ document: d2.doc, window: d2.win });assert.deepEqual(r2, { navbar: null, footer: null });-- await assert.rejects(() => mountChrome({ document: null, window: null }), /browser/);-- console.log('chrome: all checks passed');++ await assert.rejects(() => mountFrame({ document: null, window: null }), /browser/);++ console.log('frame: all checks passed');})().catch((e) => { console.error(e); process.exit(1); });@@@ -1,8 +1,8 @@--// openvibe-sdk/chrome — the OpenVibe network's shared navbar, footer, "shipped" views and theme loader for any app.++// openvibe-sdk/frame — the OpenVibe network's the OpenVibe Frame (the shared navbar, footer, "shipped" views and theme loader) for any app.--export interface ChromeLink { label: string; href: string; icon?: string; active?: boolean }--export interface ChromeMenuItem { label: string; href?: string; icon?: string; onClick?: () => void; danger?: boolean; external?: boolean }--export interface ChromeFooterConfig {++export interface FrameLink { label: string; href: string; icon?: string; active?: boolean }++export interface FrameMenuItem { label: string; href?: string; icon?: string; onClick?: () => void; danger?: boolean; external?: boolean }++export interface FrameFooterConfig {links?: Array<{ heading: string; items: Array<{ name: string; url: string }> }>;variant?: 'full' | 'compact';/** This app's update log; default: https://openvibe.network/updates?site=<host>. */@@@ -10,12 +10,12 @@ export interface ChromeFooterConfig {mount?: string;[key: string]: unknown;}--export interface ChromeOptions {++export interface FrameOptions {/** Your app's id (brand and analytics). */service?: string;brand?: { name?: string; sub?: string; tld?: string; icon?: string; variant?: string };-- links?: ChromeLink[];-- menu?: { before?: ChromeMenuItem[]; after?: ChromeMenuItem[] };++ links?: FrameLink[];++ menu?: { before?: FrameMenuItem[]; after?: FrameMenuItem[] };/** Same-origin endpoint answering { user } for your server session. */sessionUrl?: string;/** Sign-in URL; {url} = full return URL, {path} = its local path. */@@@ -24,23 +24,23 @@ export interface ChromeOptions {logoutUrl?: string;silentLogin?: string;/** Footer options, or false for no footer. */-- footer?: ChromeFooterConfig | false;++ footer?: FrameFooterConfig | false;/** false: no "shipped X ago" line in the footer. */shipped?: boolean;/** false: do not load the shared theme loader. */theme?: boolean;-- /** Where the shared chrome is served (default https://openvibe.network). */++ /** Where the Frame is served (default https://openvibe.network). */base?: string;/** Extra navbar options passed through to OpenVibeNavbar.init. */navbar?: Record<string, unknown>;document?: unknown;window?: unknown;}--export interface ChromeConfig { navbar: Record<string, unknown>; footer: Record<string, unknown> | null }--export interface ChromeUrls { theme: string; navbar: string; footer: string; shipped: string }++export interface FrameConfig { navbar: Record<string, unknown>; footer: Record<string, unknown> | null }++export interface FrameUrls { theme: string; navbar: string; footer: string; shipped: string }export const DEFAULT_BASE: string;--export function scriptUrls(opts?: { base?: string }): ChromeUrls;--export function chromeConfig(opts?: ChromeOptions): ChromeConfig;--export function chromeTags(opts?: ChromeOptions): { head: string; bodyStart: string; bodyEnd: string; config: ChromeConfig; urls: ChromeUrls };--export function mountChrome(opts?: ChromeOptions): Promise<{ navbar: unknown | null; footer: unknown | null }>;++export function scriptUrls(opts?: { base?: string }): FrameUrls;++export function frameConfig(opts?: FrameOptions): FrameConfig;++export function frameTags(opts?: FrameOptions): { head: string; bodyStart: string; bodyEnd: string; config: FrameConfig; urls: FrameUrls };++export function mountFrame(opts?: FrameOptions): Promise<{ navbar: unknown | null; footer: unknown | null }>;
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
-// Generated by scripts/esm.js from src/chrome.js. Do not edit.
-import cjs from '../src/chrome.js';
+// Generated by scripts/esm.js from src/frame.js. Do not edit.
+import cjs from '../src/frame.js';
export const {
export const {
- mountChrome, chromeTags, chromeConfig, scriptUrls, DEFAULT_BASE,
+ mountFrame, frameTags, frameConfig, scriptUrls, DEFAULT_BASE,
} = cjs;
} = cjs;
export default cjs;
export default cjs;
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
'use strict';
'use strict';
/**
/**
- * openvibe-sdk/chrome — the OpenVibe network's shared chrome for any app: the universal navbar (one
+ * openvibe-sdk/frame — the OpenVibe Frame: the navbar, footer and themes every OpenVibe site sits in, for any app: the universal navbar (one
* account menu, notifications, the site switcher, sign in and out), the shared footer (network
* account menu, notifications, the site switcher, sign in and out), the shared footer (network
* links, legal links, the "shipped X ago" line and an Updates link), the "shipped" views and the
* links, legal links, the "shipped X ago" line and an Updates link), the "shipped" views and the
* theme loader. These are the same files every OpenVibe site runs, served by OpenVibe.Network from
* theme loader. These are the same files every OpenVibe site runs, served by OpenVibe.Network from
@@ -9,8 +9,8 @@
@@ -9,8 +9,8 @@
*
*
* In the browser:
* In the browser:
*
*
- * // mountChrome from the openvibe-sdk/chrome entry (CommonJS or ESM)
- * const { navbar, footer } = await mountChrome({
+ * // mountFrame from the openvibe-sdk/frame entry (CommonJS or ESM)
+ * const { navbar, footer } = await mountFrame({
* service: 'myapp', // your app's id (brand + analytics)
* service: 'myapp', // your app's id (brand + analytics)
* brand: { name: 'My App' }, // optional brand override
* brand: { name: 'My App' }, // optional brand override
* links: [{ label: 'Home', href: '/' }, { label: 'Docs', href: '/docs' }],
* links: [{ label: 'Home', href: '/' }, { label: 'Docs', href: '/docs' }],
@@ -21,18 +21,18 @@
@@ -21,18 +21,18 @@
* footer: { links: [{ heading: 'My App', items: [{ name: 'About', url: '/about' }] }], updates: '/updates' },
* footer: { links: [{ heading: 'My App', items: [{ name: 'About', url: '/about' }] }], updates: '/updates' },
* });
* });
*
*
- * Server-rendered apps put chromeTags(opts) in <head>/<body> instead (the same markup this module
+ * Server-rendered apps put frameTags(opts) in <head>/<body> instead (the same markup this module
* would inject, as a string; the JSON config is escaped for a <script> element):
* would inject, as a string; the JSON config is escaped for a <script> element):
*
*
- * // chromeTags from the openvibe-sdk/chrome entry
- * const t = chromeTags({ service: 'myapp', links: [...] });
+ * // frameTags from the openvibe-sdk/frame entry
+ * const t = frameTags({ service: 'myapp', links: [...] });
* html = `<head>${t.head}</head><body>${t.bodyStart}…${t.bodyEnd}</body>`;
* html = `<head>${t.head}</head><body>${t.bodyStart}…${t.bodyEnd}</body>`;
*
*
* Your CSP must allow `script-src https://openvibe.network` and `connect-src https://openvibe.network`
* Your CSP must allow `script-src https://openvibe.network` and `connect-src https://openvibe.network`
* (the navbar asks /api/auth/me and the notifications there; the shipped line reads
* (the navbar asks /api/auth/me and the notifications there; the shipped line reads
* /api/v1/changelog). Signed-in state comes from the shared ov_token, else from sessionUrl. For
* /api/v1/changelog). Signed-in state comes from the shared ov_token, else from sessionUrl. For
* your own users to be recognised across the network, sign them in with OpenVibe (openvibe-sdk/auth).
* your own users to be recognised across the network, sign them in with OpenVibe (openvibe-sdk/auth).
- * The chrome is progressive: if openvibe.network is unreachable your page still renders.
+ * The Frame is progressive: if openvibe.network is unreachable your page still renders.
*/
*/
const DEFAULT_BASE = 'https://openvibe.network';
const DEFAULT_BASE = 'https://openvibe.network';
@@ -41,7 +41,7 @@ const FILES = { theme: 'theme-loader.js', navbar: 'navbar.js', footer: 'footer.j
@@ -41,7 +41,7 @@ const FILES = { theme: 'theme-loader.js', navbar: 'navbar.js', footer: 'footer.j
function baseOf(base) {
function baseOf(base) {
const b = String(base || DEFAULT_BASE).replace(/\/+$/, '');
const b = String(base || DEFAULT_BASE).replace(/\/+$/, '');
const ok = /^https:\/\/[a-z0-9.-]+(:\d+)?$/i.test(b) || /^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i.test(b);
const ok = /^https:\/\/[a-z0-9.-]+(:\d+)?$/i.test(b) || /^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i.test(b);
- if (!ok) throw new TypeError(`openvibe-sdk/chrome: base must be an https origin (or http://localhost for development), got ${base}`);
+ if (!ok) throw new TypeError(`openvibe-sdk/frame: base must be an https origin (or http://localhost for development), got ${base}`);
return b;
return b;
}
}
@@ -53,8 +53,8 @@ function scriptUrls({ base } = {}) {
@@ -53,8 +53,8 @@ function scriptUrls({ base } = {}) {
return out;
return out;
}
}
-/** The navbar and footer configurations mountChrome passes to OpenVibeNavbar.init / OpenVibeFooter.init. */
-function chromeConfig(opts = {}) {
+/** The navbar and footer configurations mountFrame passes to OpenVibeNavbar.init / OpenVibeFooter.init. */
+function frameConfig(opts = {}) {
const o = opts || {};
const o = opts || {};
const b = baseOf(o.base);
const b = baseOf(o.base);
const navbar = {
const navbar = {
@@ -85,9 +85,9 @@ const jsonForScript = (v) => JSON.stringify(v).replace(/</g, '\\u003c').replace(
@@ -85,9 +85,9 @@ const jsonForScript = (v) => JSON.stringify(v).replace(/</g, '\\u003c').replace(
const attr = (s) => String(s).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
const attr = (s) => String(s).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
/** Markup for server-rendered pages: { head, bodyStart, bodyEnd } strings. */
/** Markup for server-rendered pages: { head, bodyStart, bodyEnd } strings. */
-function chromeTags(opts = {}) {
+function frameTags(opts = {}) {
const urls = scriptUrls(opts);
const urls = scriptUrls(opts);
- const cfg = chromeConfig(opts);
+ const cfg = frameConfig(opts);
const head = [
const head = [
opts.theme === false ? '' : `<script src="${attr(urls.theme)}" defer></script>`,
opts.theme === false ? '' : `<script src="${attr(urls.theme)}" defer></script>`,
`<script src="${attr(urls.navbar)}" defer></script>`,
`<script src="${attr(urls.navbar)}" defer></script>`,
@@ -103,12 +103,12 @@ function chromeTags(opts = {}) {
@@ -103,12 +103,12 @@ function chromeTags(opts = {}) {
function loadScript(doc, src) {
function loadScript(doc, src) {
return new Promise((resolve) => {
return new Promise((resolve) => {
- const existing = doc.querySelector(`script[data-ov-chrome="${src}"]`);
+ const existing = doc.querySelector(`script[data-ov-frame="${src}"]`);
if (existing) { if (existing.dataset.loaded) resolve(true); else existing.addEventListener('load', () => resolve(true)); return; }
if (existing) { if (existing.dataset.loaded) resolve(true); else existing.addEventListener('load', () => resolve(true)); return; }
const s = doc.createElement('script');
const s = doc.createElement('script');
s.src = src;
s.src = src;
s.async = true;
s.async = true;
- s.setAttribute('data-ov-chrome', src);
+ s.setAttribute('data-ov-frame', src);
s.addEventListener('load', () => { s.dataset.loaded = '1'; resolve(true); });
s.addEventListener('load', () => { s.dataset.loaded = '1'; resolve(true); });
s.addEventListener('error', () => resolve(false));
s.addEventListener('error', () => resolve(false));
(doc.head || doc.documentElement).appendChild(s);
(doc.head || doc.documentElement).appendChild(s);
@@ -116,15 +116,15 @@ function loadScript(doc, src) {
@@ -116,15 +116,15 @@ function loadScript(doc, src) {
}
}
/**
/**
- * Browser: load the shared chrome and mount it. Resolves { navbar, footer } (the initialised
+ * Browser: load the OpenVibe Frame and mount it. Resolves { navbar, footer } (the initialised
* globals, or null for a part that could not load). Never throws for a network failure.
* globals, or null for a part that could not load). Never throws for a network failure.
*/
*/
-async function mountChrome(opts = {}) {
+async function mountFrame(opts = {}) {
const doc = (opts && opts.document) || (typeof document !== 'undefined' ? document : null);
const doc = (opts && opts.document) || (typeof document !== 'undefined' ? document : null);
const win = (opts && opts.window) || (typeof window !== 'undefined' ? window : null);
const win = (opts && opts.window) || (typeof window !== 'undefined' ? window : null);
- if (!doc || !win) throw new Error('openvibe-sdk/chrome: mountChrome runs in a browser; use chromeTags() on the server');
+ if (!doc || !win) throw new Error('openvibe-sdk/frame: mountFrame runs in a browser; use frameTags() on the server');
const urls = scriptUrls(opts);
const urls = scriptUrls(opts);
- const cfg = chromeConfig(opts);
+ const cfg = frameConfig(opts);
if (!doc.getElementById('navbar-mount')) {
if (!doc.getElementById('navbar-mount')) {
const m = doc.createElement('div');
const m = doc.createElement('div');
m.id = 'navbar-mount';
m.id = 'navbar-mount';
@@ -147,4 +147,4 @@ async function mountChrome(opts = {}) {
@@ -147,4 +147,4 @@ async function mountChrome(opts = {}) {
return { navbar, footer };
return { navbar, footer };
}
}
-module.exports = { mountChrome, chromeTags, chromeConfig, scriptUrls, DEFAULT_BASE };
+module.exports = { mountFrame, frameTags, frameConfig, scriptUrls, DEFAULT_BASE };
@@ -1,8 +1,8 @@
@@ -1,8 +1,8 @@
'use strict';
'use strict';
-// openvibe-sdk/chrome: any app mounts the network's shared navbar, footer, shipped line and themes,
+// openvibe-sdk/frame: any app mounts the network's shared navbar, footer, shipped line and themes,
// with its own session endpoints; server-rendered apps get the same markup as strings.
// with its own session endpoints; server-rendered apps get the same markup as strings.
const assert = require('node:assert/strict');
const assert = require('node:assert/strict');
-const { mountChrome, chromeTags, chromeConfig, scriptUrls } = require('../src/chrome');
+const { mountFrame, frameTags, frameConfig, scriptUrls } = require('../src/frame');
const opts = {
const opts = {
service: 'myapp',
service: 'myapp',
@@ -14,15 +14,15 @@ const opts = {
@@ -14,15 +14,15 @@ const opts = {
};
};
// Config mapping
// Config mapping
-const cfg = chromeConfig(opts);
+const cfg = frameConfig(opts);
assert.equal(cfg.navbar.service, 'myapp');
assert.equal(cfg.navbar.service, 'myapp');
assert.equal(cfg.navbar.apiBase, 'https://openvibe.network');
assert.equal(cfg.navbar.apiBase, 'https://openvibe.network');
assert.equal(cfg.navbar.logoutUrl, '/auth/logout?next={path}');
assert.equal(cfg.navbar.logoutUrl, '/auth/logout?next={path}');
assert.deepEqual(cfg.navbar.menu.before[0], { label: 'My projects', href: '/projects' });
assert.deepEqual(cfg.navbar.menu.before[0], { label: 'My projects', href: '/projects' });
assert.equal(cfg.footer.updates, '/updates');
assert.equal(cfg.footer.updates, '/updates');
assert.equal(cfg.footer.brandName, 'My App');
assert.equal(cfg.footer.brandName, 'My App');
-assert.equal(chromeConfig({ footer: false }).footer, null);
-assert.equal(chromeConfig({ shipped: false }).footer.shipped, false);
+assert.equal(frameConfig({ footer: false }).footer, null);
+assert.equal(frameConfig({ shipped: false }).footer.shipped, false);
// URLs and base validation
// URLs and base validation
assert.equal(scriptUrls().navbar, 'https://openvibe.network/shared/navbar.js');
assert.equal(scriptUrls().navbar, 'https://openvibe.network/shared/navbar.js');
@@ -31,13 +31,13 @@ assert.throws(() => scriptUrls({ base: 'http://evil.example' }), /https origin/)
@@ -31,13 +31,13 @@ assert.throws(() => scriptUrls({ base: 'http://evil.example' }), /https origin/)
assert.throws(() => scriptUrls({ base: 'javascript:alert(1)' }), /https origin/);
assert.throws(() => scriptUrls({ base: 'javascript:alert(1)' }), /https origin/);
// SSR tags: the config cannot break out of its <script>
// SSR tags: the config cannot break out of its <script>
-const t = chromeTags(opts);
+const t = frameTags(opts);
assert.ok(t.head.includes('https://openvibe.network/shared/navbar.js') && t.head.includes('theme-loader.js') && t.head.includes('footer.js'));
assert.ok(t.head.includes('https://openvibe.network/shared/navbar.js') && t.head.includes('theme-loader.js') && t.head.includes('footer.js'));
assert.equal(t.bodyStart, '<div id="navbar-mount"></div>');
assert.equal(t.bodyStart, '<div id="navbar-mount"></div>');
assert.ok(t.bodyEnd.includes('<footer id="ov-footer"></footer>'));
assert.ok(t.bodyEnd.includes('<footer id="ov-footer"></footer>'));
assert.ok(!/<\/script><script>alert/.test(t.bodyEnd), 'a label cannot close the init script');
assert.ok(!/<\/script><script>alert/.test(t.bodyEnd), 'a label cannot close the init script');
assert.equal((t.bodyEnd.match(/<\/script>/g) || []).length, 1);
assert.equal((t.bodyEnd.match(/<\/script>/g) || []).length, 1);
-assert.ok(!chromeTags({ theme: false }).head.includes('theme-loader'));
+assert.ok(!frameTags({ theme: false }).head.includes('theme-loader'));
// Browser mount with a minimal DOM: scripts "load", globals appear, init receives the config.
// Browser mount with a minimal DOM: scripts "load", globals appear, init receives the config.
function fakeDom() {
function fakeDom() {
@@ -67,7 +67,7 @@ function fakeDom() {
@@ -67,7 +67,7 @@ function fakeDom() {
(async () => {
(async () => {
const d = fakeDom();
const d = fakeDom();
- const r = await mountChrome({ ...opts, document: d.doc, window: d.win });
+ const r = await mountFrame({ ...opts, document: d.doc, window: d.win });
assert.ok(r.navbar && r.footer);
assert.ok(r.navbar && r.footer);
assert.equal(d.inits.navbar.sessionUrl, '/auth/me');
assert.equal(d.inits.navbar.sessionUrl, '/auth/me');
assert.equal(d.inits.footer.mount, '#ov-footer');
assert.equal(d.inits.footer.mount, '#ov-footer');
@@ -77,8 +77,8 @@ function fakeDom() {
@@ -77,8 +77,8 @@ function fakeDom() {
// A failed load leaves that part null and never throws.
// A failed load leaves that part null and never throws.
const d2 = fakeDom();
const d2 = fakeDom();
d2.doc.head.appendChild = function (c) { d2.nodes.push(c); setImmediate(() => (c.listeners.error || []).forEach((f) => f())); return c; };
d2.doc.head.appendChild = function (c) { d2.nodes.push(c); setImmediate(() => (c.listeners.error || []).forEach((f) => f())); return c; };
- const r2 = await mountChrome({ document: d2.doc, window: d2.win });
+ const r2 = await mountFrame({ document: d2.doc, window: d2.win });
assert.deepEqual(r2, { navbar: null, footer: null });
assert.deepEqual(r2, { navbar: null, footer: null });
- await assert.rejects(() => mountChrome({ document: null, window: null }), /browser/);
- console.log('chrome: all checks passed');
+ await assert.rejects(() => mountFrame({ document: null, window: null }), /browser/);
+ console.log('frame: all checks passed');
})().catch((e) => { console.error(e); process.exit(1); });
})().catch((e) => { console.error(e); process.exit(1); });
@@ -1,8 +1,8 @@
@@ -1,8 +1,8 @@
-// openvibe-sdk/chrome — the OpenVibe network's shared navbar, footer, "shipped" views and theme loader for any app.
+// openvibe-sdk/frame — the OpenVibe network's the OpenVibe Frame (the shared navbar, footer, "shipped" views and theme loader) for any app.
-export interface ChromeLink { label: string; href: string; icon?: string; active?: boolean }
-export interface ChromeMenuItem { label: string; href?: string; icon?: string; onClick?: () => void; danger?: boolean; external?: boolean }
-export interface ChromeFooterConfig {
+export interface FrameLink { label: string; href: string; icon?: string; active?: boolean }
+export interface FrameMenuItem { label: string; href?: string; icon?: string; onClick?: () => void; danger?: boolean; external?: boolean }
+export interface FrameFooterConfig {
links?: Array<{ heading: string; items: Array<{ name: string; url: string }> }>;
links?: Array<{ heading: string; items: Array<{ name: string; url: string }> }>;
variant?: 'full' | 'compact';
variant?: 'full' | 'compact';
/** This app's update log; default: https://openvibe.network/updates?site=<host>. */
/** This app's update log; default: https://openvibe.network/updates?site=<host>. */
@@ -10,12 +10,12 @@ export interface ChromeFooterConfig {
@@ -10,12 +10,12 @@ export interface ChromeFooterConfig {
mount?: string;
mount?: string;
[key: string]: unknown;
[key: string]: unknown;
}
}
-export interface ChromeOptions {
+export interface FrameOptions {
/** Your app's id (brand and analytics). */
/** Your app's id (brand and analytics). */
service?: string;
service?: string;
brand?: { name?: string; sub?: string; tld?: string; icon?: string; variant?: string };
brand?: { name?: string; sub?: string; tld?: string; icon?: string; variant?: string };
- links?: ChromeLink[];
- menu?: { before?: ChromeMenuItem[]; after?: ChromeMenuItem[] };
+ links?: FrameLink[];
+ menu?: { before?: FrameMenuItem[]; after?: FrameMenuItem[] };
/** Same-origin endpoint answering { user } for your server session. */
/** Same-origin endpoint answering { user } for your server session. */
sessionUrl?: string;
sessionUrl?: string;
/** Sign-in URL; {url} = full return URL, {path} = its local path. */
/** Sign-in URL; {url} = full return URL, {path} = its local path. */
@@ -24,23 +24,23 @@ export interface ChromeOptions {
@@ -24,23 +24,23 @@ export interface ChromeOptions {
logoutUrl?: string;
logoutUrl?: string;
silentLogin?: string;
silentLogin?: string;
/** Footer options, or false for no footer. */
/** Footer options, or false for no footer. */
- footer?: ChromeFooterConfig | false;
+ footer?: FrameFooterConfig | false;
/** false: no "shipped X ago" line in the footer. */
/** false: no "shipped X ago" line in the footer. */
shipped?: boolean;
shipped?: boolean;
/** false: do not load the shared theme loader. */
/** false: do not load the shared theme loader. */
theme?: boolean;
theme?: boolean;
- /** Where the shared chrome is served (default https://openvibe.network). */
+ /** Where the Frame is served (default https://openvibe.network). */
base?: string;
base?: string;
/** Extra navbar options passed through to OpenVibeNavbar.init. */
/** Extra navbar options passed through to OpenVibeNavbar.init. */
navbar?: Record<string, unknown>;
navbar?: Record<string, unknown>;
document?: unknown;
document?: unknown;
window?: unknown;
window?: unknown;
}
}
-export interface ChromeConfig { navbar: Record<string, unknown>; footer: Record<string, unknown> | null }
-export interface ChromeUrls { theme: string; navbar: string; footer: string; shipped: string }
+export interface FrameConfig { navbar: Record<string, unknown>; footer: Record<string, unknown> | null }
+export interface FrameUrls { theme: string; navbar: string; footer: string; shipped: string }
export const DEFAULT_BASE: string;
export const DEFAULT_BASE: string;
-export function scriptUrls(opts?: { base?: string }): ChromeUrls;
-export function chromeConfig(opts?: ChromeOptions): ChromeConfig;
-export function chromeTags(opts?: ChromeOptions): { head: string; bodyStart: string; bodyEnd: string; config: ChromeConfig; urls: ChromeUrls };
-export function mountChrome(opts?: ChromeOptions): Promise<{ navbar: unknown | null; footer: unknown | null }>;
+export function scriptUrls(opts?: { base?: string }): FrameUrls;
+export function frameConfig(opts?: FrameOptions): FrameConfig;
+export function frameTags(opts?: FrameOptions): { head: string; bodyStart: string; bodyEnd: string; config: FrameConfig; urls: FrameUrls };
+export function mountFrame(opts?: FrameOptions): Promise<{ navbar: unknown | null; footer: unknown | null }>;