openvibers/openvibe.coupons Commits

openvibe-shared v1.5.0 (Track R release manifest), openvibe-contracts v0.32.0, openvibe-sdk v0.5.0

OP OpenVibers committed 23 Sept 2026 · a6a86b2ead · 7 files, +29 −23

- /release.json is mounted with release.mount(app, { registry }): POST /release-metrics takes open
  tabs' update reports into /metrics (release_client_updates_total). On contracts 0.32.0 the
  manifest carries the release-manifest 1.1.0 fields; no components are declared, so every
  release still prompts a reload. test/pages-discovery.test.js validates the served manifest
  against the contracts.
- identity.service-token-claims 1.2.0 (contracts 0.26.0) requires project_id and env on app tokens:
  the mock Network's app tokens carry them, as Network's do. serviceAuth now refuses a sandbox app
  token itself with token.sandbox_refused, the code Coupons already answered; no coupons.*
  capability can be granted to a developer app (all first-party).
- SDK 0.2.2 -> 0.5.0: Coupons uses the token client, createClient and the events outbox, none of
  which changed behaviour. README and STATUS list the new pins.

Changes

ddiff --git a/README.md b/README.md
iindex 01abc0d..03b6bf3 100644
---- a/README.md
++++ b/README.md
@@@ -278,8 +278,8 @@ Coupons-local and aren't capabilities.
## Depends on
- **Packages** (all pinned by release tarball): `openvibe-publishing` v0.2.1 (seo gate, ssr,
-- index-hooks), `openvibe-contracts` v0.25.0, `openvibe-shared` v1.3.0 (chrome, app icon, footer,
-- legal, release, metrics, ready, seo), `openvibe-sdk` v0.2.2 (events outbox, service tokens).
++ index-hooks), `openvibe-contracts` v0.32.0, `openvibe-shared` v1.5.0 (chrome, app icon, footer,
++ legal, release, metrics, ready, seo), `openvibe-sdk` v0.5.0 (events outbox, service tokens).
- **OpenVibe.Network:**
- SSO: an OAuth client `coupons` with redirect `https://openvibe.coupons/auth/callback`
(registered in production).
ddiff --git a/STATUS.json b/STATUS.json
iindex 5fee67e..c93f87d 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -9,9 +9,9 @@
"domain": "openvibe.coupons",
"packages": [
"openvibe-publishing v0.2.1",
-- "openvibe-contracts v0.25.0",
-- "openvibe-shared v1.3.0",
-- "openvibe-sdk v0.2.2"
++ "openvibe-contracts v0.32.0",
++ "openvibe-shared v1.5.0",
++ "openvibe-sdk v0.5.0"
],
"capabilities": [
"coupons.coupon.submit",
ddiff --git a/package-lock.json b/package-lock.json
iindex 2890118..08ee3cb 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -15,10 +15,10 @@
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.25.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",
"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v0.2.1",
-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.2.2",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"
++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"
},
"devDependencies": {
"jsonwebtoken": "^9.0.2"
@@@ -1015,9 +1015,9 @@
}
},
"node_modules/openvibe-contracts": {
-- "version": "0.25.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.25.0",
-- "integrity": "sha512-0gw97v/DbRI9F2YL2o3QT9uq3CYw/7iaiEIsgUJHjGspLtRH7SGFu5tG92xWldCZmexwYFucU04QIbdtZycTWA==",
++ "version": "0.32.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",
++ "integrity": "sha512-ntN58SIG03YDxbhsiIzOM5GRH19jRMHZUYWI3xU2VwovExagF5DdwApLVfezsu84UTJ+V2Jfn+LWDc+Oz8ab+g==",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
@@@ -1060,18 +1060,18 @@
}
},
"node_modules/openvibe-sdk": {
-- "version": "0.2.2",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.2.2",
-- "integrity": "sha512-kDifUp3EcmnTWzElAmaCZnX659WErcnm4+qVEoAiYQWX/w/1p4TWvoHbUPsrX65TSROVR3tiyVkl801n/0yyCw==",
++ "version": "0.5.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
++ "integrity": "sha512-QVJkR5iazQX0ZwIrDp2A88ZeYYBSXEmSzhpgURPUSnzezIM3jz0HBhVKowyhkvg2aBO61XNf77eb4zJGkMjZiw==",
"license": "MIT",
"engines": {
"node": ">=20"
}
},
"node_modules/openvibe-shared": {
-- "version": "1.3.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0",
-- "integrity": "sha512-w8yl/5+9eend8I2O/DzAJnEAL2N3GTUY7c7pzK+MLJuLAGD8FmJJQ9gwbgyrq70Snjt3ebrg3TlpORhRAp2kgA==",
++ "version": "1.5.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0",
++ "integrity": "sha512-cehnscuYMkJwuCYGcI6wPSswbUn0bhTyGAaD5feNCoO//2e9gyC7cgjkEcIOtM8DQF3yTuUNyXnOwckIeM4fUg==",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0"
ddiff --git a/package.json b/package.json
iindex 2c147eb..c70484b 100644
---- a/package.json
++++ b/package.json
@@@ -20,10 +20,10 @@
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.25.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",
"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v0.2.1",
-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.2.2",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"
++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"
},
"devDependencies": {
"jsonwebtoken": "^9.0.2"
ddiff --git a/server/app.js b/server/app.js
iindex b575eb1..6845efe 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -95,7 +95,8 @@ function createApp(opts = {}) {
// ── Machine endpoints ───────────────────────────────────
app.get('/api/health', (_req, res) => res.json({ status: 'ok', service: 'openvibe-coupons', version: VERSION }));
-- app.get('/release.json', release.handler);
++ // GET /release.json (ADR-016) and POST /release-metrics: open tabs' update reports into /metrics.
++ release.mount(app, { registry: metrics.registry });
const readiness = createCouponsReadiness({ store, auth, outbox, worker, importer, reports, config, release: release.release });
app.get('/api/ready', readiness.handler);
ddiff --git a/test/helpers/mocks.js b/test/helpers/mocks.js
iindex 0e0c728..21986ba 100644
---- a/test/helpers/mocks.js
++++ b/test/helpers/mocks.js
@@@ -52,8 +52,10 @@ async function startNetwork() {
return json(404, { error: 'not found' });
});
issuer = srv.url;
++ // An app token carries its developer project and env, as Network's do (identity.service-token-claims 1.2.0).
function signService({ sub, aud, cap, actorType = 'service', extra = {} }) {
-- return serviceAuth.signServiceToken({ iss: issuer, sub, actor_type: actorType, aud, cap, iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 300, jti: crypto.randomUUID(), ...extra }, privatePem);
++ const app = actorType === 'app' ? { project_id: 'prj_01J8ZQ4Y7N3M2K1H0G9F8E7D6C', env: 'production' } : {};
++ return serviceAuth.signServiceToken({ iss: issuer, sub, actor_type: actorType, aud, cap, iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 300, jti: crypto.randomUUID(), ...app, ...extra }, privatePem);
}
function addUser(username, extra = {}) {
return { subject: ids.newId('user'), username, display_name: extra.display_name || username, role: extra.role || 'user' };
ddiff --git a/test/pages-discovery.test.js b/test/pages-discovery.test.js
iindex 16265a1..0bff723 100644
---- a/test/pages-discovery.test.js
++++ b/test/pages-discovery.test.js
@@@ -115,7 +115,10 @@ const { boot, check, done } = require('./helpers/boot');
const body = ready.json();
assert.match(JSON.stringify(body), /relay off/);
assert.match(JSON.stringify(body), /worker off/);
-- assert.strictEqual((await t.get('/release.json')).status, 200);
++ const rel = await t.get('/release.json');
++ assert.strictEqual(rel.status, 200);
++ assert.deepStrictEqual(require('openvibe-contracts').validate('registry.release-manifest@1', rel.json()).errors, []);
++ assert.strictEqual(rel.json().metrics_url, '/release-metrics');
assert.strictEqual((await t.get('/nope')).status, 404);
assert.strictEqual((await t.get('/m/nope')).status, 404);
assert.strictEqual((await t.get('/c/cpn_00000000000000000000000000')).status, 404);