openvibers/openvibe.sites Commits
Every placeholder publishes /release.json (the shared release watcher asked each page for it and got a 404): service, and a release id that is the hash of the page with dates neutralised; nginx serves it; the test checks it and each footer's shipped line
Changes
@@@ -3,7 +3,7 @@/*** OpenVibe.Sites — static front pages for the network's domains that are not full apps yet.*-- * node build.js → dist/<domain>/{index.html,robots.txt,sitemap.xml,manifest.webmanifest}++ * node build.js → dist/<domain>/{index.html,robots.txt,sitemap.xml,manifest.webmanifest,status.json,release.json}* node build.js --check → exit 1 when dist/ is stale (CI / deploy guard)** One template, one catalog (sites.json), one snapshot of facts (facts.json, refreshed by@@@ -396,6 +396,11 @@ server {add_header Access-Control-Allow-Origin "*";${sec}}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ ${sec}++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -465,6 +470,20 @@ function liveStatus(site) {`;}++/**++ * /release.json: what the shared release watcher (openvibe-shared release-watch, loaded by the navbar++ * on every page) asks each site for. A placeholder's release is the hash of its page with the dates++ * neutralised, so it changes exactly when the page does.++ */++function releaseJson(site, html) {++ const release = require('crypto').createHash('sha256').update(String(html).replace(/\d{4}-\d{2}-\d{2}/g, 'DATE')).digest('hex').slice(0, 12);++ return JSON.stringify({++ service: site.tld, release, released_at: `${today}T00:00:00.000Z`, booted_at: null, contracts_version: null,++ packages: { 'openvibe-shared': require('openvibe-shared/package.json').version },++ min_client_release: null, mixed_version_window_hours: 24, kind: 'placeholder',++ }, null, 2) + '\n';++}++/** /status.json: the same facts as the page, for scripts (CORS-open). */function statusJson(site) {const f = repoFacts(site);@@@ -488,6 +507,7 @@ function build() {const out = {};for (const site of catalog.sites) {out[`${site.domain}/index.html`] = page(site);++ out[`${site.domain}/release.json`] = releaseJson(site, out[`${site.domain}/index.html`]);out[`${site.domain}/404.html`] = notFound(site);out[`${site.domain}/robots.txt`] = robots(site);if (!site.kind) out[`${site.domain}/sitemap.xml`] = sitemap(site);@@@ -63,6 +63,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -63,6 +63,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -63,6 +63,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -63,6 +63,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -73,6 +73,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -63,6 +63,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -63,6 +63,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;@@@ -63,6 +63,14 @@ server {add_header Referrer-Policy strict-origin-when-cross-origin always;add_header Strict-Transport-Security "max-age=31536000" always;}++ location = /release.json {++ add_header Cache-Control "no-cache";++ add_header Access-Control-Allow-Origin "*";++ add_header X-Content-Type-Options nosniff always;++ add_header X-Frame-Options SAMEORIGIN always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000" always;++ }# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.error_page 404 /404.html;nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "admin",++ "release": "15932573ac66",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "ai",++ "release": "729f35e13916",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "api",++ "release": "0da0ea18ab49",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "auth",++ "release": "88d4486c6aa8",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "stream",++ "release": "7217c3153adb",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "chat",++ "release": "d703c838dff3",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "coupons",++ "release": "7ffe4f0f5631",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "deals",++ "release": "36af1bd4aa33",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "host",++ "release": "b6a20f0674b9",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "news",++ "release": "019aa335b9ae",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "reviews",++ "release": "4093d27e2681",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "tips",++ "release": "3a6c853a9f2a",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "trade",++ "release": "af685650dda9",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "vip",++ "release": "05fbd1e56222",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "realtime",++ "release": "7df3c64d664f",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "status",++ "release": "6b4db8562da8",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "service": "themes",++ "release": "80e1b0a48d84",++ "released_at": "2026-09-24T00:00:00.000Z",++ "booted_at": null,++ "contracts_version": null,++ "packages": {++ "openvibe-shared": "1.11.1"++ },++ "min_client_release": null,++ "mixed_version_window_hours": 24,++ "kind": "placeholder"++}@@@ -37,6 +37,13 @@ for (const site of catalog.sites) {for (const f of ['robots.txt', 'manifest.webmanifest', 'status.json', ...(site.kind ? [] : ['sitemap.xml'])]) {assert.ok(fs.existsSync(path.join(ROOT, 'dist', d, f)), `${d}: ${f}`);}++ // The shared release watcher asks every page for /release.json; a placeholder publishes one, and its++ // footer carries its own service's "shipped" line (the OpenVibe Frame).++ const rel = JSON.parse(read(`dist/${d}/release.json`));++ assert.strictEqual(rel.service, site.tld, `${d}: release.json service`);++ assert.match(rel.release, /^[0-9a-f]{12}$/, `${d}: release.json release`);++ assert.match(read(`deploy/nginx/${d}.conf`), /location = \/release\.json \{/, `${d}: nginx serves /release.json`);++ assert.ok(html.includes(`data-ov-shipped="latest" data-service="${site.tld}"`), `${d}: the footer's shipped line`);if (site.kind) {// A notice (a closed product, a pointer) is not indexed, has no sitemap and is not listed as opening.assert.match(html, /<meta name="robots" content="noindex, follow">/, `${d}: notice is noindex`);
@@ -3,7 +3,7 @@
@@ -3,7 +3,7 @@
/**
/**
* OpenVibe.Sites — static front pages for the network's domains that are not full apps yet.
* OpenVibe.Sites — static front pages for the network's domains that are not full apps yet.
*
*
- * node build.js → dist/<domain>/{index.html,robots.txt,sitemap.xml,manifest.webmanifest}
+ * node build.js → dist/<domain>/{index.html,robots.txt,sitemap.xml,manifest.webmanifest,status.json,release.json}
* node build.js --check → exit 1 when dist/ is stale (CI / deploy guard)
* node build.js --check → exit 1 when dist/ is stale (CI / deploy guard)
*
*
* One template, one catalog (sites.json), one snapshot of facts (facts.json, refreshed by
* One template, one catalog (sites.json), one snapshot of facts (facts.json, refreshed by
@@ -396,6 +396,11 @@ server {
@@ -396,6 +396,11 @@ server {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Origin "*";
${sec}
${sec}
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ ${sec}
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -465,6 +470,20 @@ function liveStatus(site) {
@@ -465,6 +470,20 @@ function liveStatus(site) {
`;
`;
}
}
+/**
+ * /release.json: what the shared release watcher (openvibe-shared release-watch, loaded by the navbar
+ * on every page) asks each site for. A placeholder's release is the hash of its page with the dates
+ * neutralised, so it changes exactly when the page does.
+ */
+function releaseJson(site, html) {
+ const release = require('crypto').createHash('sha256').update(String(html).replace(/\d{4}-\d{2}-\d{2}/g, 'DATE')).digest('hex').slice(0, 12);
+ return JSON.stringify({
+ service: site.tld, release, released_at: `${today}T00:00:00.000Z`, booted_at: null, contracts_version: null,
+ packages: { 'openvibe-shared': require('openvibe-shared/package.json').version },
+ min_client_release: null, mixed_version_window_hours: 24, kind: 'placeholder',
+ }, null, 2) + '\n';
+}
+
/** /status.json: the same facts as the page, for scripts (CORS-open). */
/** /status.json: the same facts as the page, for scripts (CORS-open). */
function statusJson(site) {
function statusJson(site) {
const f = repoFacts(site);
const f = repoFacts(site);
@@ -488,6 +507,7 @@ function build() {
@@ -488,6 +507,7 @@ function build() {
const out = {};
const out = {};
for (const site of catalog.sites) {
for (const site of catalog.sites) {
out[`${site.domain}/index.html`] = page(site);
out[`${site.domain}/index.html`] = page(site);
+ out[`${site.domain}/release.json`] = releaseJson(site, out[`${site.domain}/index.html`]);
out[`${site.domain}/404.html`] = notFound(site);
out[`${site.domain}/404.html`] = notFound(site);
out[`${site.domain}/robots.txt`] = robots(site);
out[`${site.domain}/robots.txt`] = robots(site);
if (!site.kind) out[`${site.domain}/sitemap.xml`] = sitemap(site);
if (!site.kind) out[`${site.domain}/sitemap.xml`] = sitemap(site);
@@ -63,6 +63,14 @@ server {
@@ -63,6 +63,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -63,6 +63,14 @@ server {
@@ -63,6 +63,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -63,6 +63,14 @@ server {
@@ -63,6 +63,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -63,6 +63,14 @@ server {
@@ -63,6 +63,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -73,6 +73,14 @@ server {
@@ -73,6 +73,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -63,6 +63,14 @@ server {
@@ -63,6 +63,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -63,6 +63,14 @@ server {
@@ -63,6 +63,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
@@ -63,6 +63,14 @@ server {
@@ -63,6 +63,14 @@ server {
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Strict-Transport-Security "max-age=31536000" always;
}
}
+ location = /release.json {
+ add_header Cache-Control "no-cache";
+ add_header Access-Control-Allow-Origin "*";
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000" always;
+ }
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
# Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
error_page 404 /404.html;
error_page 404 /404.html;
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "admin",
+ "release": "15932573ac66",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "ai",
+ "release": "729f35e13916",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "api",
+ "release": "0da0ea18ab49",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "auth",
+ "release": "88d4486c6aa8",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "stream",
+ "release": "7217c3153adb",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "chat",
+ "release": "d703c838dff3",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "coupons",
+ "release": "7ffe4f0f5631",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "deals",
+ "release": "36af1bd4aa33",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "host",
+ "release": "b6a20f0674b9",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "news",
+ "release": "019aa335b9ae",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "reviews",
+ "release": "4093d27e2681",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "tips",
+ "release": "3a6c853a9f2a",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "trade",
+ "release": "af685650dda9",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "vip",
+ "release": "05fbd1e56222",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "realtime",
+ "release": "7df3c64d664f",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "status",
+ "release": "6b4db8562da8",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "service": "themes",
+ "release": "80e1b0a48d84",
+ "released_at": "2026-09-24T00:00:00.000Z",
+ "booted_at": null,
+ "contracts_version": null,
+ "packages": {
+ "openvibe-shared": "1.11.1"
+ },
+ "min_client_release": null,
+ "mixed_version_window_hours": 24,
+ "kind": "placeholder"
+}
@@ -37,6 +37,13 @@ for (const site of catalog.sites) {
@@ -37,6 +37,13 @@ for (const site of catalog.sites) {
for (const f of ['robots.txt', 'manifest.webmanifest', 'status.json', ...(site.kind ? [] : ['sitemap.xml'])]) {
for (const f of ['robots.txt', 'manifest.webmanifest', 'status.json', ...(site.kind ? [] : ['sitemap.xml'])]) {
assert.ok(fs.existsSync(path.join(ROOT, 'dist', d, f)), `${d}: ${f}`);
assert.ok(fs.existsSync(path.join(ROOT, 'dist', d, f)), `${d}: ${f}`);
}
}
+ // The shared release watcher asks every page for /release.json; a placeholder publishes one, and its
+ // footer carries its own service's "shipped" line (the OpenVibe Frame).
+ const rel = JSON.parse(read(`dist/${d}/release.json`));
+ assert.strictEqual(rel.service, site.tld, `${d}: release.json service`);
+ assert.match(rel.release, /^[0-9a-f]{12}$/, `${d}: release.json release`);
+ assert.match(read(`deploy/nginx/${d}.conf`), /location = \/release\.json \{/, `${d}: nginx serves /release.json`);
+ assert.ok(html.includes(`data-ov-shipped="latest" data-service="${site.tld}"`), `${d}: the footer's shipped line`);
if (site.kind) {
if (site.kind) {
// A notice (a closed product, a pointer) is not indexed, has no sitemap and is not listed as opening.
// A notice (a closed product, a pointer) is not indexed, has no sitemap and is not listed as opening.
assert.match(html, /<meta name="robots" content="noindex, follow">/, `${d}: notice is noindex`);
assert.match(html, /<meta name="robots" content="noindex, follow">/, `${d}: notice is noindex`);