openvibers/openvibe.reviews Commits
openvibe-shared v1.5.0 (Track R release manifest), openvibe-contracts v0.32.0, openvibe-sdk v0.5.0
Changes
@@@ -193,7 +193,7 @@ by editors; signals arrive only from Sources.- OpenVibe.Community — discussion threads (`community.comment.write`)- OpenVibe.AI — optional, proposes summaries through `reviews.summary.propose`- OpenVibe.Search — consumes the index events--- packages: openvibe-publishing v0.2.1, openvibe-contracts v0.20.0, openvibe-shared v1.3.0, openvibe-sdk v0.4.0++- packages: openvibe-publishing v0.2.1, openvibe-contracts v0.32.0, openvibe-shared v1.5.0, openvibe-sdk v0.5.0## Acceptance (tested in `test/`)@@@ -7,8 +7,8 @@"runtime": "node 22, express, better-sqlite3","port": 4830,"domain": "openvibe.reviews",-- "contracts": "openvibe-contracts v0.20.0",-- "packages": ["openvibe-publishing v0.2.1", "openvibe-shared v1.3.0", "openvibe-sdk v0.4.0"],++ "contracts": "openvibe-contracts v0.32.0",++ "packages": ["openvibe-publishing v0.2.1", "openvibe-shared v1.5.0", "openvibe-sdk v0.5.0"],"capabilities": ["reviews.entity.resolve", "reviews.entity.manage", "reviews.entity.merge", "reviews.entity.split", "reviews.signal.import", "reviews.summary.propose", "reviews.summary.publish", "reviews.correction.submit"],"capabilitiesRegistered": true,"deployed": true,@@@ -14,10 +14,10 @@"dotenv": "^16.4.7","express": "^4.21.2","express-rate-limit": "^7.5.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.20.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.4.0",-- "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"},"engines": {"node": ">=22.0.0"@@@ -999,9 +999,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.20.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.20.0",-- "integrity": "sha512-0sUKklq2taKCInqh83ys8akGOaZ4KemDWW5Dy4HdLI4dtL6Jd7RUBaEytsJKcISIza2dSPXb7V+xC3jJUuSDow==",++ "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",@@@ -1044,18 +1044,18 @@}},"node_modules/openvibe-sdk": {-- "version": "0.4.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.4.0",-- "integrity": "sha512-Hyz55YOMIs0xwCQ1NXsN0sUYhlbKXAX3uWY2fPCdEEL18FXUYVhK8wlG/5JMMypFHnjDiSd9VHzBNG12hWsmkQ==",++ "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"@@@ -19,10 +19,10 @@"dotenv": "^16.4.7","express": "^4.21.2","express-rate-limit": "^7.5.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.20.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.4.0",-- "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"},"engines": {"node": ">=22.0.0"@@@ -78,7 +78,8 @@ function createApp({ config, svc, viewers, platform, sync, keys, db, log = consoapp.use('/internal', http.middleware(), consumer.router);app.get('/api/health', (_req, res) => res.json({ status: 'ok', service: 'openvibe-reviews', 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 ready = createReadiness({service: 'reviews', release: release.release,checks: [@@@ -31,10 +31,14 @@ function userToken({ subject: sub = READER, username = 'someone', role = 'user',const editorToken = () => userToken({ subject: EDITOR, username: 'editor' });const readerToken = () => userToken({ subject: READER, username: 'reader' });--/** A principal token: svc:<client> by default; `sub` + `actorType` for an app:/mod: principal, `extra` for on_behalf_of, env… */++/**++ * A principal token: svc:<client> by default; `sub` + `actorType` for an app:/mod: principal, `extra` for on_behalf_of, env…++ * An app token carries its developer project and env, as Network's do (identity.service-token-claims 1.2.0).++ */function serviceToken({ client = 'ai', cap = [], aud = 'openvibe.reviews', exp = 300, sub, actorType = 'service', extra = {} } = {}) {const now = Math.floor(Date.now() / 1000);-- return serviceAuth.signServiceToken({ iss: ISSUER, sub: sub || `svc:${client}`, actor_type: actorType, aud: [aud], cap, ns: [], iat: now, exp: now + exp, jti: `tok_${crypto.randomBytes(8).toString('hex')}`, ...extra }, privateKey);++ const app = actorType === 'app' ? { project_id: 'prj_01J8ZQ4Y7N3M2K1H0G9F8E7D6C', env: 'production' } : {};++ return serviceAuth.signServiceToken({ iss: ISSUER, sub: sub || `svc:${client}`, actor_type: actorType, aud: [aud], cap, ns: [], iat: now, exp: now + exp, jti: `tok_${crypto.randomBytes(8).toString('hex')}`, ...app, ...extra }, privateKey);}const quiet = { log() {}, warn() {}, error() {} };@@@ -88,6 +88,11 @@ t('API identity: bad tokens are refused, never downgraded; editor actions need aassert.strictEqual(ready.status, 200);assert.strictEqual(ready.json.ready, true);assert.strictEqual((await req(h, 'GET', '/api/health')).json.service, 'openvibe-reviews');++ // The release manifest (registry.release-manifest@1) names where open tabs report updates.++ const rel = await req(h, 'GET', '/release.json');++ assert.strictEqual(rel.json.service, 'reviews');++ assert.deepStrictEqual(require('openvibe-contracts').validate('registry.release-manifest@1', rel.json).errors, []);++ assert.strictEqual(rel.json.metrics_url, '/release-metrics');const nf = await req(h, 'GET', '/api/v1/nope');assert.strictEqual(nf.status, 404);assert.match(nf.headers.get('content-type'), /problem\+json/);
@@ -193,7 +193,7 @@ by editors; signals arrive only from Sources.
@@ -193,7 +193,7 @@ by editors; signals arrive only from Sources.
- OpenVibe.Community — discussion threads (`community.comment.write`)
- OpenVibe.Community — discussion threads (`community.comment.write`)
- OpenVibe.AI — optional, proposes summaries through `reviews.summary.propose`
- OpenVibe.AI — optional, proposes summaries through `reviews.summary.propose`
- OpenVibe.Search — consumes the index events
- OpenVibe.Search — consumes the index events
-- packages: openvibe-publishing v0.2.1, openvibe-contracts v0.20.0, openvibe-shared v1.3.0, openvibe-sdk v0.4.0
+- packages: openvibe-publishing v0.2.1, openvibe-contracts v0.32.0, openvibe-shared v1.5.0, openvibe-sdk v0.5.0
## Acceptance (tested in `test/`)
## Acceptance (tested in `test/`)
@@ -7,8 +7,8 @@
@@ -7,8 +7,8 @@
"runtime": "node 22, express, better-sqlite3",
"runtime": "node 22, express, better-sqlite3",
"port": 4830,
"port": 4830,
"domain": "openvibe.reviews",
"domain": "openvibe.reviews",
- "contracts": "openvibe-contracts v0.20.0",
- "packages": ["openvibe-publishing v0.2.1", "openvibe-shared v1.3.0", "openvibe-sdk v0.4.0"],
+ "contracts": "openvibe-contracts v0.32.0",
+ "packages": ["openvibe-publishing v0.2.1", "openvibe-shared v1.5.0", "openvibe-sdk v0.5.0"],
"capabilities": ["reviews.entity.resolve", "reviews.entity.manage", "reviews.entity.merge", "reviews.entity.split", "reviews.signal.import", "reviews.summary.propose", "reviews.summary.publish", "reviews.correction.submit"],
"capabilities": ["reviews.entity.resolve", "reviews.entity.manage", "reviews.entity.merge", "reviews.entity.split", "reviews.signal.import", "reviews.summary.propose", "reviews.summary.publish", "reviews.correction.submit"],
"capabilitiesRegistered": true,
"capabilitiesRegistered": true,
"deployed": true,
"deployed": true,
@@ -14,10 +14,10 @@
@@ -14,10 +14,10 @@
"dotenv": "^16.4.7",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.20.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-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.4.0",
- "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"
},
},
"engines": {
"engines": {
"node": ">=22.0.0"
"node": ">=22.0.0"
@@ -999,9 +999,9 @@
@@ -999,9 +999,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.20.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.20.0",
- "integrity": "sha512-0sUKklq2taKCInqh83ys8akGOaZ4KemDWW5Dy4HdLI4dtL6Jd7RUBaEytsJKcISIza2dSPXb7V+xC3jJUuSDow==",
+ "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",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1044,18 +1044,18 @@
@@ -1044,18 +1044,18 @@
}
}
},
},
"node_modules/openvibe-sdk": {
"node_modules/openvibe-sdk": {
- "version": "0.4.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.4.0",
- "integrity": "sha512-Hyz55YOMIs0xwCQ1NXsN0sUYhlbKXAX3uWY2fPCdEEL18FXUYVhK8wlG/5JMMypFHnjDiSd9VHzBNG12hWsmkQ==",
+ "version": "0.5.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
+ "integrity": "sha512-QVJkR5iazQX0ZwIrDp2A88ZeYYBSXEmSzhpgURPUSnzezIM3jz0HBhVKowyhkvg2aBO61XNf77eb4zJGkMjZiw==",
"license": "MIT",
"license": "MIT",
"engines": {
"engines": {
"node": ">=20"
"node": ">=20"
}
}
},
},
"node_modules/openvibe-shared": {
"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",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -19,10 +19,10 @@
@@ -19,10 +19,10 @@
"dotenv": "^16.4.7",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.20.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-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.4.0",
- "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"
},
},
"engines": {
"engines": {
"node": ">=22.0.0"
"node": ">=22.0.0"
@@ -78,7 +78,8 @@ function createApp({ config, svc, viewers, platform, sync, keys, db, log = conso
@@ -78,7 +78,8 @@ function createApp({ config, svc, viewers, platform, sync, keys, db, log = conso
app.use('/internal', http.middleware(), consumer.router);
app.use('/internal', http.middleware(), consumer.router);
app.get('/api/health', (_req, res) => res.json({ status: 'ok', service: 'openvibe-reviews', version: VERSION }));
app.get('/api/health', (_req, res) => res.json({ status: 'ok', service: 'openvibe-reviews', 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 ready = createReadiness({
const ready = createReadiness({
service: 'reviews', release: release.release,
service: 'reviews', release: release.release,
checks: [
checks: [
@@ -31,10 +31,14 @@ function userToken({ subject: sub = READER, username = 'someone', role = 'user',
@@ -31,10 +31,14 @@ function userToken({ subject: sub = READER, username = 'someone', role = 'user',
const editorToken = () => userToken({ subject: EDITOR, username: 'editor' });
const editorToken = () => userToken({ subject: EDITOR, username: 'editor' });
const readerToken = () => userToken({ subject: READER, username: 'reader' });
const readerToken = () => userToken({ subject: READER, username: 'reader' });
-/** A principal token: svc:<client> by default; `sub` + `actorType` for an app:/mod: principal, `extra` for on_behalf_of, env… */
+/**
+ * A principal token: svc:<client> by default; `sub` + `actorType` for an app:/mod: principal, `extra` for on_behalf_of, env…
+ * An app token carries its developer project and env, as Network's do (identity.service-token-claims 1.2.0).
+ */
function serviceToken({ client = 'ai', cap = [], aud = 'openvibe.reviews', exp = 300, sub, actorType = 'service', extra = {} } = {}) {
function serviceToken({ client = 'ai', cap = [], aud = 'openvibe.reviews', exp = 300, sub, actorType = 'service', extra = {} } = {}) {
const now = Math.floor(Date.now() / 1000);
const now = Math.floor(Date.now() / 1000);
- return serviceAuth.signServiceToken({ iss: ISSUER, sub: sub || `svc:${client}`, actor_type: actorType, aud: [aud], cap, ns: [], iat: now, exp: now + exp, jti: `tok_${crypto.randomBytes(8).toString('hex')}`, ...extra }, privateKey);
+ const app = actorType === 'app' ? { project_id: 'prj_01J8ZQ4Y7N3M2K1H0G9F8E7D6C', env: 'production' } : {};
+ return serviceAuth.signServiceToken({ iss: ISSUER, sub: sub || `svc:${client}`, actor_type: actorType, aud: [aud], cap, ns: [], iat: now, exp: now + exp, jti: `tok_${crypto.randomBytes(8).toString('hex')}`, ...app, ...extra }, privateKey);
}
}
const quiet = { log() {}, warn() {}, error() {} };
const quiet = { log() {}, warn() {}, error() {} };
@@ -88,6 +88,11 @@ t('API identity: bad tokens are refused, never downgraded; editor actions need a
@@ -88,6 +88,11 @@ t('API identity: bad tokens are refused, never downgraded; editor actions need a
assert.strictEqual(ready.status, 200);
assert.strictEqual(ready.status, 200);
assert.strictEqual(ready.json.ready, true);
assert.strictEqual(ready.json.ready, true);
assert.strictEqual((await req(h, 'GET', '/api/health')).json.service, 'openvibe-reviews');
assert.strictEqual((await req(h, 'GET', '/api/health')).json.service, 'openvibe-reviews');
+ // The release manifest (registry.release-manifest@1) names where open tabs report updates.
+ const rel = await req(h, 'GET', '/release.json');
+ assert.strictEqual(rel.json.service, 'reviews');
+ assert.deepStrictEqual(require('openvibe-contracts').validate('registry.release-manifest@1', rel.json).errors, []);
+ assert.strictEqual(rel.json.metrics_url, '/release-metrics');
const nf = await req(h, 'GET', '/api/v1/nope');
const nf = await req(h, 'GET', '/api/v1/nope');
assert.strictEqual(nf.status, 404);
assert.strictEqual(nf.status, 404);
assert.match(nf.headers.get('content-type'), /problem\+json/);
assert.match(nf.headers.get('content-type'), /problem\+json/);