openvibers/openvibe.publishing Commits
Forward the AI summary, facts, updated and url through renderDocument
Changes
@@@ -4,6 +4,24 @@ All notable changes to `openvibe-publishing`. Versions follow [semver](https://sbreaking change to any exported function, table layout, reason code or document shape is a newmajor (a minor while 0.x). A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball.++## 1.3.0 — 2026-10-04++++**Additive: `layout.renderDocument` now forwards the AI summary to the shell.** No existing export,++table layout, reason code or document shape changes; a product on v1.2.0 upgrades by pinning v1.3.0.++Every option defaults to what the product already renders, so a product that passes none of them is++byte-identical.++++### Changed — `openvibe-publishing/layout`++++- `renderDocument(o)` passes four options through to `shell.page`, which builds them with++ `seo.pageSummary`: **`summary`** (one-line AI summary), **`facts`** (`[[key, value], string]` or++ `[string]` rows), **`updated`** (ISO date) and **`url`** (absolute; defaults to the document's++ canonical). With a summary the document carries `<meta name="ai-summary">` and a `WebPage` JSON-LD++ tag in `<head>`, and with facts the `<noscript><section data-ai-summary>` block at the top of++ `<body>`; `updated` becomes `dateModified`. Nothing is invented: without a summary none of that is++ emitted, an omitted fact or date is left out of the block, and `url` is only ever the caller's or++ the canonical's. Styling is unchanged.++## 1.2.0 — 2026-10-04**Additive: one new entry point, `openvibe-publishing/layout`.** No existing export, table layout, reason@@@ -53,6 +53,27 @@ The five content products (News, Reviews, Deals, Coupons, Trade) each delete the- **Test:** the product's own suite (`ov test`), including its ingest and publication tests; thePublishing side is `test/ingest.test.js` and `test/publication.test.js`.++## v1.2.0 → v1.3.0++++Additive: `layout.renderDocument` forwards the AI summary to the shell. Everything else is unchanged,++and a product that passes none of the four options renders exactly as on v1.2.0.++++- **package.json:** `"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.3.0"`.++ `openvibe-shared` stays at v2.6.0 or later (that is where `seo.pageSummary` and the shell's `summary`++ support already live).++- **Lockfile:** copy the `node_modules/openvibe-publishing` entry (`1.3.0`, the matching `resolved`,++ the real `integrity`) from a product already on the tag.++- **What changes in the product:** pass `summary` on the home render (and on any other page that has++ a real one-line summary, e.g. an article or a review), so the `ai-summary` meta lands on the page.++ - `layout.renderDocument({ …, summary, facts, updated, url })` — `summary` is the one-line AI++ summary, `facts` the `[[key, value], string]` (or `[string]`) rows, `updated` the ISO date, `url`++ absolute and defaulting to the canonical.++ - `summary` alone gives `<meta name="ai-summary">` plus a `WebPage` JSON-LD tag; `facts` gives the++ `<noscript><section data-ai-summary>` block; `updated` becomes `dateModified`.++ - **Only pass what the facts support.** A missing rating, price, date or author is left out of the++ facts block, never defaulted — the same rule as every other structured output here.++- **Test:** the product's page/render tests (`ov test`); Publishing's side is `test/layout.test.js`.++## v1.1.0 → v1.2.0Adds `openvibe-publishing/layout`; nothing existing changes. The seven publication sites each hand-wrote@@@ -37,6 +37,8 @@ function json(value) {* o: site (service id, e.g. 'news'), service (defaults to site), siteName ('OpenVibe.News'), lang,* title (composed by the caller), description, canonical (absolute), type, image, imageAlt,* author, keywords, locale, twitterSite, jsonLd [], prev, next, published, modified,++ * summary (one-line AI summary), facts ([[key, value], string] or [string]), updated (ISO date),++ * url (absolute; defaults to the canonical),* decision (or robots), feeds [{ type, href, title }], navbar, footer, navLinks, home,* css (stylesheet href), styles [openvibe-shared/serve names], scripts [<script> HTML],* referrer, release, head (extra head HTML), iconSite, skipLabel, account (noscript account-bar@@@ -98,6 +100,14 @@ document.addEventListener('DOMContentLoaded', function () {description: o.description,canonical,robots,++ // The shell builds the AI summary itself (seo.pageSummary): `summary` puts the ai-summary++ // meta and a WebPage JSON-LD tag in <head>, `facts` the <noscript data-ai-summary> block at++ // the top of <body>, and `url`/`updated` feed it. Nothing is invented: without a summary the++ // shell emits none, and an omitted fact or date is left out of the block.++ summary: o.summary || undefined,++ facts: o.facts || undefined,++ updated: o.updated || undefined,++ url: o.url || canonical,type: o.type || 'website',image: o.image || undefined,imageAlt: o.imageAlt || undefined,@@@ -1,6 +1,6 @@{"name": "openvibe-publishing",-- "version": "1.2.0",++ "version": "1.3.0","description": "Shared publishing packages for the OpenVibe publication products, on PostgreSQL through openvibe-sdk/db: revisions, scheduling, taxonomy, citations, media attachments, discussion references, the SEO indexability gate, authorship, index hooks, SSR helpers, the ingest chassis (Sources client, change cursor, signed event consumer, normalisers, PSL) and the publication glue. Packages, not an authority.","main": "index.js","exports": {@@@ -85,4 +85,26 @@ test('no release, referrer, css or account: those extras are left out', () => {assert.match(out, /window\.__OV_PAGE = \{"navbar":\{"service":"blog"\},"footer":\{"service":"blog","variant":"compact"\}\}/);});++test('summary reaches the shell, so the page carries the ai-summary meta, the facts block and the url', () => {++ const out = renderDocument(doc({++ summary: 'A story, in one line.', facts: [['Rating', '4/5'], ['Published', '2026-10-01']],++ updated: '2026-10-02T09:00:00Z',++ }));++ assert.match(out, /<meta name="ai-summary" content="A story, in one line\.">/);++ assert.match(out, /<noscript><section data-ai-summary><h2>A story · OpenVibe\.News<\/h2><p>A story, in one line\.<\/p><ul><li>Rating: 4\/5<\/li><li>Published: 2026-10-01<\/li><\/ul><\/section><\/noscript>/);++ assert.match(out, /"dateModified":"2026-10-02T09:00:00\.000Z"/);++ assert.match(out, /"url":"https:\/\/openvibe\.news\/a\/original"/);++});++++test('without a summary no ai-summary meta, facts block or WebPage tag is invented', () => {++ const out = renderDocument(doc());++ assert.doesNotMatch(out, /ai-summary|data-ai-summary|"@type":"WebPage"/);++});++++test('url defaults to the canonical, so an explicit url wins', () => {++ const own = renderDocument(doc({ summary: 'S', url: 'https://openvibe.news/a/explicit' }));++ assert.match(own, /"url":"https:\/\/openvibe\.news\/a\/explicit"/);++ assert.doesNotMatch(own, /"url":"https:\/\/openvibe\.news\/a\/original"/);++});++run();
@@ -4,6 +4,24 @@ All notable changes to `openvibe-publishing`. Versions follow [semver](https://s
@@ -4,6 +4,24 @@ All notable changes to `openvibe-publishing`. Versions follow [semver](https://s
breaking change to any exported function, table layout, reason code or document shape is a new
breaking change to any exported function, table layout, reason code or document shape is a new
major (a minor while 0.x). A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball.
major (a minor while 0.x). A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball.
+## 1.3.0 — 2026-10-04
+
+**Additive: `layout.renderDocument` now forwards the AI summary to the shell.** No existing export,
+table layout, reason code or document shape changes; a product on v1.2.0 upgrades by pinning v1.3.0.
+Every option defaults to what the product already renders, so a product that passes none of them is
+byte-identical.
+
+### Changed — `openvibe-publishing/layout`
+
+- `renderDocument(o)` passes four options through to `shell.page`, which builds them with
+ `seo.pageSummary`: **`summary`** (one-line AI summary), **`facts`** (`[[key, value], string]` or
+ `[string]` rows), **`updated`** (ISO date) and **`url`** (absolute; defaults to the document's
+ canonical). With a summary the document carries `<meta name="ai-summary">` and a `WebPage` JSON-LD
+ tag in `<head>`, and with facts the `<noscript><section data-ai-summary>` block at the top of
+ `<body>`; `updated` becomes `dateModified`. Nothing is invented: without a summary none of that is
+ emitted, an omitted fact or date is left out of the block, and `url` is only ever the caller's or
+ the canonical's. Styling is unchanged.
+
## 1.2.0 — 2026-10-04
## 1.2.0 — 2026-10-04
**Additive: one new entry point, `openvibe-publishing/layout`.** No existing export, table layout, reason
**Additive: one new entry point, `openvibe-publishing/layout`.** No existing export, table layout, reason
@@ -53,6 +53,27 @@ The five content products (News, Reviews, Deals, Coupons, Trade) each delete the
@@ -53,6 +53,27 @@ The five content products (News, Reviews, Deals, Coupons, Trade) each delete the
- **Test:** the product's own suite (`ov test`), including its ingest and publication tests; the
- **Test:** the product's own suite (`ov test`), including its ingest and publication tests; the
Publishing side is `test/ingest.test.js` and `test/publication.test.js`.
Publishing side is `test/ingest.test.js` and `test/publication.test.js`.
+## v1.2.0 → v1.3.0
+
+Additive: `layout.renderDocument` forwards the AI summary to the shell. Everything else is unchanged,
+and a product that passes none of the four options renders exactly as on v1.2.0.
+
+- **package.json:** `"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.3.0"`.
+ `openvibe-shared` stays at v2.6.0 or later (that is where `seo.pageSummary` and the shell's `summary`
+ support already live).
+- **Lockfile:** copy the `node_modules/openvibe-publishing` entry (`1.3.0`, the matching `resolved`,
+ the real `integrity`) from a product already on the tag.
+- **What changes in the product:** pass `summary` on the home render (and on any other page that has
+ a real one-line summary, e.g. an article or a review), so the `ai-summary` meta lands on the page.
+ - `layout.renderDocument({ …, summary, facts, updated, url })` — `summary` is the one-line AI
+ summary, `facts` the `[[key, value], string]` (or `[string]`) rows, `updated` the ISO date, `url`
+ absolute and defaulting to the canonical.
+ - `summary` alone gives `<meta name="ai-summary">` plus a `WebPage` JSON-LD tag; `facts` gives the
+ `<noscript><section data-ai-summary>` block; `updated` becomes `dateModified`.
+ - **Only pass what the facts support.** A missing rating, price, date or author is left out of the
+ facts block, never defaulted — the same rule as every other structured output here.
+- **Test:** the product's page/render tests (`ov test`); Publishing's side is `test/layout.test.js`.
+
## v1.1.0 → v1.2.0
## v1.1.0 → v1.2.0
Adds `openvibe-publishing/layout`; nothing existing changes. The seven publication sites each hand-wrote
Adds `openvibe-publishing/layout`; nothing existing changes. The seven publication sites each hand-wrote
@@ -37,6 +37,8 @@ function json(value) {
@@ -37,6 +37,8 @@ function json(value) {
* o: site (service id, e.g. 'news'), service (defaults to site), siteName ('OpenVibe.News'), lang,
* o: site (service id, e.g. 'news'), service (defaults to site), siteName ('OpenVibe.News'), lang,
* title (composed by the caller), description, canonical (absolute), type, image, imageAlt,
* title (composed by the caller), description, canonical (absolute), type, image, imageAlt,
* author, keywords, locale, twitterSite, jsonLd [], prev, next, published, modified,
* author, keywords, locale, twitterSite, jsonLd [], prev, next, published, modified,
+ * summary (one-line AI summary), facts ([[key, value], string] or [string]), updated (ISO date),
+ * url (absolute; defaults to the canonical),
* decision (or robots), feeds [{ type, href, title }], navbar, footer, navLinks, home,
* decision (or robots), feeds [{ type, href, title }], navbar, footer, navLinks, home,
* css (stylesheet href), styles [openvibe-shared/serve names], scripts [<script> HTML],
* css (stylesheet href), styles [openvibe-shared/serve names], scripts [<script> HTML],
* referrer, release, head (extra head HTML), iconSite, skipLabel, account (noscript account-bar
* referrer, release, head (extra head HTML), iconSite, skipLabel, account (noscript account-bar
@@ -98,6 +100,14 @@ document.addEventListener('DOMContentLoaded', function () {
@@ -98,6 +100,14 @@ document.addEventListener('DOMContentLoaded', function () {
description: o.description,
description: o.description,
canonical,
canonical,
robots,
robots,
+ // The shell builds the AI summary itself (seo.pageSummary): `summary` puts the ai-summary
+ // meta and a WebPage JSON-LD tag in <head>, `facts` the <noscript data-ai-summary> block at
+ // the top of <body>, and `url`/`updated` feed it. Nothing is invented: without a summary the
+ // shell emits none, and an omitted fact or date is left out of the block.
+ summary: o.summary || undefined,
+ facts: o.facts || undefined,
+ updated: o.updated || undefined,
+ url: o.url || canonical,
type: o.type || 'website',
type: o.type || 'website',
image: o.image || undefined,
image: o.image || undefined,
imageAlt: o.imageAlt || undefined,
imageAlt: o.imageAlt || undefined,
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-publishing",
"name": "openvibe-publishing",
- "version": "1.2.0",
+ "version": "1.3.0",
"description": "Shared publishing packages for the OpenVibe publication products, on PostgreSQL through openvibe-sdk/db: revisions, scheduling, taxonomy, citations, media attachments, discussion references, the SEO indexability gate, authorship, index hooks, SSR helpers, the ingest chassis (Sources client, change cursor, signed event consumer, normalisers, PSL) and the publication glue. Packages, not an authority.",
"description": "Shared publishing packages for the OpenVibe publication products, on PostgreSQL through openvibe-sdk/db: revisions, scheduling, taxonomy, citations, media attachments, discussion references, the SEO indexability gate, authorship, index hooks, SSR helpers, the ingest chassis (Sources client, change cursor, signed event consumer, normalisers, PSL) and the publication glue. Packages, not an authority.",
"main": "index.js",
"main": "index.js",
"exports": {
"exports": {
@@ -85,4 +85,26 @@ test('no release, referrer, css or account: those extras are left out', () => {
@@ -85,4 +85,26 @@ test('no release, referrer, css or account: those extras are left out', () => {
assert.match(out, /window\.__OV_PAGE = \{"navbar":\{"service":"blog"\},"footer":\{"service":"blog","variant":"compact"\}\}/);
assert.match(out, /window\.__OV_PAGE = \{"navbar":\{"service":"blog"\},"footer":\{"service":"blog","variant":"compact"\}\}/);
});
});
+test('summary reaches the shell, so the page carries the ai-summary meta, the facts block and the url', () => {
+ const out = renderDocument(doc({
+ summary: 'A story, in one line.', facts: [['Rating', '4/5'], ['Published', '2026-10-01']],
+ updated: '2026-10-02T09:00:00Z',
+ }));
+ assert.match(out, /<meta name="ai-summary" content="A story, in one line\.">/);
+ assert.match(out, /<noscript><section data-ai-summary><h2>A story · OpenVibe\.News<\/h2><p>A story, in one line\.<\/p><ul><li>Rating: 4\/5<\/li><li>Published: 2026-10-01<\/li><\/ul><\/section><\/noscript>/);
+ assert.match(out, /"dateModified":"2026-10-02T09:00:00\.000Z"/);
+ assert.match(out, /"url":"https:\/\/openvibe\.news\/a\/original"/);
+});
+
+test('without a summary no ai-summary meta, facts block or WebPage tag is invented', () => {
+ const out = renderDocument(doc());
+ assert.doesNotMatch(out, /ai-summary|data-ai-summary|"@type":"WebPage"/);
+});
+
+test('url defaults to the canonical, so an explicit url wins', () => {
+ const own = renderDocument(doc({ summary: 'S', url: 'https://openvibe.news/a/explicit' }));
+ assert.match(own, /"url":"https:\/\/openvibe\.news\/a\/explicit"/);
+ assert.doesNotMatch(own, /"url":"https:\/\/openvibe\.news\/a\/original"/);
+});
+
run();
run();