openvibers/openvibe.codes Commits
Codes' outbox is openvibe-sdk/events createServiceOutbox (plan T1): the same event_outbox table, event-type allowlist and relay rules in one call, the hand-rolled wrapper gone. Pins openvibe-sdk v0.25.0, openvibe-contracts v0.79.0, openvibe-shared v2.0.0. Both suites green (verified).
Changes
@@@ -48,7 +48,7 @@ Everything is server-rendered and usable without JavaScript. The only scripts of- **OpenVibe.Events** — the outbox relay publishes `codes.app.*` with Codes' service token (`events.event.publish`); the Events playground calls it with the app's token (`events.app.publish`); the project archive pulls the project's app events with a Network export token (`events.app.read`).- **OpenVibe.Media** — the Media playground uploads with the app's token into the project's namespace; the project archive lists the project's objects, namespaces and download URLs with a Network export token (`media.object.list`, `media.object.read`).- **PostgreSQL 18 and Valkey 9** (OpenVibe.Host `roles/data/`, ADR-035): every read and write is async through `openvibe-sdk/db`; Valkey holds the per-actor limit counters (optional).--- **openvibe-contracts** v0.76.0, **openvibe-sdk v0.20.1**, **openvibe-shared v1.27.0** (pinned tag tarballs; the docs show the tag and the package version, and say so when they differ).++- **openvibe-contracts** v0.79.0, **openvibe-sdk v0.25.0**, **openvibe-shared v2.0.0** (pinned tag tarballs; the docs show the tag and the package version, and say so when they differ).No path in Codes sends or accepts a shared loopback key (tested by grep and at runtime).@@@ -7,11 +7,11 @@"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db, async) + Valkey 9","port": 4900,"domain": "openvibe.codes",-- "contracts": "openvibe-contracts v0.76.0",++ "contracts": "openvibe-contracts v0.79.0","packages": [-- "openvibe-contracts v0.76.0",-- "openvibe-sdk v0.20.1",-- "openvibe-shared v1.27.0"++ "openvibe-contracts v0.79.0",++ "openvibe-sdk v0.25.0",++ "openvibe-shared v2.0.0"],"summary": "Deployed and public since 2026-09-23: https://openvibe.codes is served by this portal (deployed with `sudo ovhost deploy codes`, unit openvibe-codes on 127.0.0.1:4900 behind nginx), and the domain left OpenVibe.Sites. Sign-in uses the registered Network OAuth client `codes` (PKCE S256); the principal `codes` holds events.event.publish and the Events relay is configured. On 2026-09-23 production held 0 manifests, 0 releases and 0 playground runs. The external-developer path (account, project, sandbox app, grants, Media upload, app events) was run once on production on 2026-09-23 with public endpoints and curl, not through these playgrounds; that check is not committed to a repository or CI.","works": [@@@ -16,9 +16,9 @@"express-rate-limit": "^7.5.0","helmet": "^8.1.0","iovalkey": "^0.4.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.76.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.23.1",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.29.2",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.25.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.0.0","pg": "^8.23.0"},"devDependencies": {@@@ -1157,9 +1157,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.76.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.76.0",-- "integrity": "sha512-zTdx776KWrcJKm1JTnf+0+BnsvswdvXy1ldPyse1hSqhnW/SSr98sX696eRetFUhTl8qVM1xSRCykqIUbqItPw==",++ "version": "0.79.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",++ "integrity": "sha512-FY5GLbiOq0kDw5lUPPzX6h3ATbzvsr/mX5DUCF4GRfn6vGMP0p89rZZDhgtVDaS+yCBCYMZ8zFaZxuHEkEVwVg==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1173,9 +1173,9 @@}},"node_modules/openvibe-sdk": {-- "version": "0.23.1",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.23.1",-- "integrity": "sha512-6u1Ym8u+nSKyq+uJjel4/3QDQ9HOjpQzFa8oMasorXQhgHUlDEQ7X6F6Xf1zZTuuVdMVkxOeqJqOnQbraktosg==",++ "version": "0.25.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.25.0",++ "integrity": "sha512-8eurZLyFUtB8XN16hLUuYkah+qwS3tkvdzul5qdZkJTi0aZ4q8dq2QGWSq6pN5mtVPdJyuJ8Mo53r36lixmP1g==","license": "MIT","engines": {"node": ">=20"@@@ -1202,9 +1202,9 @@}},"node_modules/openvibe-shared": {-- "version": "1.29.2",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.29.2",-- "integrity": "sha512-w5daRSzujokYT1/Q3fu0KdcSZUBEv1pm66H8vIAI2SY0xtOaLqgfkXJpfIz1NgvJuyK8dhXBbLjM8Ow9RZ5x+A==",++ "version": "2.0.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.0.0",++ "integrity": "sha512-n5CHC0hfxOo0jo/3kS4O3KiwhirhZDlx5zZTIuPyIf3Dq0yI/R6R64btx7NYwZe5dI0NjvqzEtnv19m8unwLMg==","license": "MIT","dependencies": {"jsonwebtoken": "^9.0.0"@@@ -28,9 +28,9 @@"express-rate-limit": "^7.5.0","helmet": "^8.1.0","iovalkey": "^0.4.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.76.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.23.1",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.29.2",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.25.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.0.0","pg": "^8.23.0"},"engines": {@@@ -15,69 +15,26 @@* audience openvibe.events) only when EVENTS_URL and OV_OAUTH_CLIENT_SECRET are set; otherwise rows* wait in event_outbox and /api/ready reports the relay as off. Payloads never carry a secret.*/--const { createClient } = require('openvibe-sdk/core');--const { createServiceTokenClient } = require('openvibe-sdk/auth');--const { createEventsClient, createPgOutbox } = require('openvibe-sdk/events');++const { createServiceOutbox } = require('openvibe-sdk/events');const EVENT_TYPES = ['codes.app.published', 'codes.app.deprecated', 'codes.app.revoked', 'codes.moderation.action'];++/** Codes' wiring of the SDK's shared outbox (plan T1). */function createCodesOutbox({ db, config, fetchImpl, now, log = console }) {-- const enabled = Boolean(config.events.url && config.oauth.clientSecret);-- const clientOpts = { baseUrls: { events: config.events.url || 'http://127.0.0.1:4300' }, retries: 0, autoDiscover: false };-- if (fetchImpl) clientOpts.fetch = fetchImpl;-- if (enabled) {-- clientOpts.tokenProvider = createServiceTokenClient({-- tokenUrl: `${config.networkInternalUrl}/oauth/token`, clientId: config.oauth.clientId, clientSecret: config.oauth.clientSecret,-- scope: { 'openvibe.events': 'events.event.publish' }, ...(fetchImpl ? { fetch: fetchImpl } : {}),-- });-- } else {-- clientOpts.getToken = async () => { throw new Error('events relay disabled (EVENTS_URL / OV_OAUTH_CLIENT_SECRET unset)'); };-- }-- const events = createEventsClient(createClient(clientOpts), { source: 'codes' });-- let lastError = null;-- // The PostgreSQL outbox: rows are written in the change's own transaction (enqueue(db, …) joins the ambient-- // transaction); several processes relay one table safely (leases).-- const outbox = createPgOutbox(db, {-- events,++ return createServiceOutbox({++ db,++ source: 'codes',++ eventsUrl: config.events.url,++ networkInternalUrl: config.networkInternalUrl,++ clientId: config.oauth.clientId,++ clientSecret: config.oauth.clientSecret,intervalMs: config.events.intervalMs,-- now,-- onError: (err) => {-- const msg = err && err.message;-- if (msg !== lastError) log.warn('[Codes] event publish failed (will retry):', msg);-- lastError = msg;-- },++ log,++ eventTypes: EVENT_TYPES,++ autoDiscover: false,++ ...(fetchImpl ? { fetch: fetchImpl } : {}),++ ...(now ? { now } : {}),});---- /** Inside the caller's transaction. Returns the complete envelope (with its event_id). */-- async function emit(envelope, { traceparent } = {}) {-- if (!EVENT_TYPES.includes(envelope.event_type)) throw new Error(`Codes does not produce ${envelope.event_type}`);-- return await outbox.enqueue(db, envelope, { traceparent });-- }---- /**-- * codes.moderation.action (common.moderation-action@1), inside the caller's transaction. actor:-- * the staff member's subject; target: { type, id, owner_subject? }. Never the content.-- */-- async function moderationAction({ action, target, actorSubject, reason = null, details = {} }, { traceparent } = {}) {-- const t = { type: target.type, id: String(target.id).slice(0, 200), owner_subject: target.owner_subject || null };-- return await emit({-- event_type: 'codes.moderation.action',-- actor: actorSubject ? { type: 'user', id: actorSubject } : { type: 'service', id: 'codes' },-- subject: { type: 'moderation_action', id: `${t.type}:${t.id}`.slice(0, 200) },-- visibility: 'internal',-- payload: { action, target: t, actor_subject: actorSubject || null, reason: reason ? String(reason).slice(0, 500) : null, details: details || {} },-- }, { traceparent });-- }---- return {-- emit,-- moderationAction,-- outbox,-- enabled,-- start() { if (enabled) outbox.start(); },-- stop: () => outbox.stop(),-- status: async () => ({ enabled, pending: await outbox.pending(), rejected: await outbox.rejected(), last_error: lastError }),-- };}module.exports = { createCodesOutbox, EVENT_TYPES };@@@ -19,11 +19,11 @@ const sdkPkg = require('openvibe-sdk/package.json');await check('package.json pins the contracts, SDK and shared tags that are installed', async () => {const tag = (dep) => (pkg.dependencies[dep].match(/refs\/tags\/(v[0-9.]+)$/) || [])[1];-- assert.strictEqual(tag('openvibe-contracts'), 'v0.76.0');++ assert.strictEqual(tag('openvibe-contracts'), 'v0.79.0');assert.strictEqual(tag('openvibe-sdk'), `v${sdkPkg.version}`);assert.strictEqual(tag('openvibe-shared'), `v${require('openvibe-shared/package.json').version}`);-- assert.strictEqual(sdkPkg.version, '0.23.1');-- // The contracts tag v0.76.0 carries the released codes manifest and codes.app-manifest@1.++ assert.strictEqual(sdkPkg.version, '0.25.0');++ // The contracts tag v0.79.0 carries the released codes manifest and codes.app-manifest@1.assert.ok(contracts.services.get('codes') && contracts.services.get('codes').status === 'alpha');assert.ok(contracts.resolve('codes.app-manifest@1'));});@@@ -33,13 +33,13 @@ const sdkPkg = require('openvibe-sdk/package.json');const r = await t.get(p);assert.strictEqual(r.status, 200, p);assert.ok(r.text.includes(`openvibe-contracts v${contractsVersion}`), `${p} names the installed contracts version`);-- assert.ok(r.text.includes('OpenVibe.Contracts/tree/v0.76.0'), `${p} links the pinned tag`);-- if (contractsVersion !== '0.76.0') assert.ok(r.text.includes('(tag v0.76.0)'), `${p} says the tag differs from the package version`);++ assert.ok(r.text.includes('OpenVibe.Contracts/tree/v0.79.0'), `${p} links the pinned tag`);++ if (contractsVersion !== '0.79.0') assert.ok(r.text.includes('(tag v0.79.0)'), `${p} says the tag differs from the package version`);assert.ok(r.text.includes(`openvibe-sdk v${sdkPkg.version}`), `${p} names the SDK version`);}const v = (await t.get('/api/v1/docs/versions')).json();assert.strictEqual(v.contracts, contractsVersion);-- assert.strictEqual(v.contracts_tag, 'v0.76.0');++ assert.strictEqual(v.contracts_tag, 'v0.79.0');assert.strictEqual(v.sdk, sdkPkg.version);});
@@ -48,7 +48,7 @@ Everything is server-rendered and usable without JavaScript. The only scripts of
@@ -48,7 +48,7 @@ Everything is server-rendered and usable without JavaScript. The only scripts of
- **OpenVibe.Events** — the outbox relay publishes `codes.app.*` with Codes' service token (`events.event.publish`); the Events playground calls it with the app's token (`events.app.publish`); the project archive pulls the project's app events with a Network export token (`events.app.read`).
- **OpenVibe.Events** — the outbox relay publishes `codes.app.*` with Codes' service token (`events.event.publish`); the Events playground calls it with the app's token (`events.app.publish`); the project archive pulls the project's app events with a Network export token (`events.app.read`).
- **OpenVibe.Media** — the Media playground uploads with the app's token into the project's namespace; the project archive lists the project's objects, namespaces and download URLs with a Network export token (`media.object.list`, `media.object.read`).
- **OpenVibe.Media** — the Media playground uploads with the app's token into the project's namespace; the project archive lists the project's objects, namespaces and download URLs with a Network export token (`media.object.list`, `media.object.read`).
- **PostgreSQL 18 and Valkey 9** (OpenVibe.Host `roles/data/`, ADR-035): every read and write is async through `openvibe-sdk/db`; Valkey holds the per-actor limit counters (optional).
- **PostgreSQL 18 and Valkey 9** (OpenVibe.Host `roles/data/`, ADR-035): every read and write is async through `openvibe-sdk/db`; Valkey holds the per-actor limit counters (optional).
-- **openvibe-contracts** v0.76.0, **openvibe-sdk v0.20.1**, **openvibe-shared v1.27.0** (pinned tag tarballs; the docs show the tag and the package version, and say so when they differ).
+- **openvibe-contracts** v0.79.0, **openvibe-sdk v0.25.0**, **openvibe-shared v2.0.0** (pinned tag tarballs; the docs show the tag and the package version, and say so when they differ).
No path in Codes sends or accepts a shared loopback key (tested by grep and at runtime).
No path in Codes sends or accepts a shared loopback key (tested by grep and at runtime).
@@ -7,11 +7,11 @@
@@ -7,11 +7,11 @@
"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db, async) + Valkey 9",
"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db, async) + Valkey 9",
"port": 4900,
"port": 4900,
"domain": "openvibe.codes",
"domain": "openvibe.codes",
- "contracts": "openvibe-contracts v0.76.0",
+ "contracts": "openvibe-contracts v0.79.0",
"packages": [
"packages": [
- "openvibe-contracts v0.76.0",
- "openvibe-sdk v0.20.1",
- "openvibe-shared v1.27.0"
+ "openvibe-contracts v0.79.0",
+ "openvibe-sdk v0.25.0",
+ "openvibe-shared v2.0.0"
],
],
"summary": "Deployed and public since 2026-09-23: https://openvibe.codes is served by this portal (deployed with `sudo ovhost deploy codes`, unit openvibe-codes on 127.0.0.1:4900 behind nginx), and the domain left OpenVibe.Sites. Sign-in uses the registered Network OAuth client `codes` (PKCE S256); the principal `codes` holds events.event.publish and the Events relay is configured. On 2026-09-23 production held 0 manifests, 0 releases and 0 playground runs. The external-developer path (account, project, sandbox app, grants, Media upload, app events) was run once on production on 2026-09-23 with public endpoints and curl, not through these playgrounds; that check is not committed to a repository or CI.",
"summary": "Deployed and public since 2026-09-23: https://openvibe.codes is served by this portal (deployed with `sudo ovhost deploy codes`, unit openvibe-codes on 127.0.0.1:4900 behind nginx), and the domain left OpenVibe.Sites. Sign-in uses the registered Network OAuth client `codes` (PKCE S256); the principal `codes` holds events.event.publish and the Events relay is configured. On 2026-09-23 production held 0 manifests, 0 releases and 0 playground runs. The external-developer path (account, project, sandbox app, grants, Media upload, app events) was run once on production on 2026-09-23 with public endpoints and curl, not through these playgrounds; that check is not committed to a repository or CI.",
"works": [
"works": [
@@ -16,9 +16,9 @@
@@ -16,9 +16,9 @@
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.76.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.23.1",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.29.2",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.25.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.0.0",
"pg": "^8.23.0"
"pg": "^8.23.0"
},
},
"devDependencies": {
"devDependencies": {
@@ -1157,9 +1157,9 @@
@@ -1157,9 +1157,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.76.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.76.0",
- "integrity": "sha512-zTdx776KWrcJKm1JTnf+0+BnsvswdvXy1ldPyse1hSqhnW/SSr98sX696eRetFUhTl8qVM1xSRCykqIUbqItPw==",
+ "version": "0.79.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
+ "integrity": "sha512-FY5GLbiOq0kDw5lUPPzX6h3ATbzvsr/mX5DUCF4GRfn6vGMP0p89rZZDhgtVDaS+yCBCYMZ8zFaZxuHEkEVwVg==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1173,9 +1173,9 @@
@@ -1173,9 +1173,9 @@
}
}
},
},
"node_modules/openvibe-sdk": {
"node_modules/openvibe-sdk": {
- "version": "0.23.1",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.23.1",
- "integrity": "sha512-6u1Ym8u+nSKyq+uJjel4/3QDQ9HOjpQzFa8oMasorXQhgHUlDEQ7X6F6Xf1zZTuuVdMVkxOeqJqOnQbraktosg==",
+ "version": "0.25.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.25.0",
+ "integrity": "sha512-8eurZLyFUtB8XN16hLUuYkah+qwS3tkvdzul5qdZkJTi0aZ4q8dq2QGWSq6pN5mtVPdJyuJ8Mo53r36lixmP1g==",
"license": "MIT",
"license": "MIT",
"engines": {
"engines": {
"node": ">=20"
"node": ">=20"
@@ -1202,9 +1202,9 @@
@@ -1202,9 +1202,9 @@
}
}
},
},
"node_modules/openvibe-shared": {
"node_modules/openvibe-shared": {
- "version": "1.29.2",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.29.2",
- "integrity": "sha512-w5daRSzujokYT1/Q3fu0KdcSZUBEv1pm66H8vIAI2SY0xtOaLqgfkXJpfIz1NgvJuyK8dhXBbLjM8Ow9RZ5x+A==",
+ "version": "2.0.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.0.0",
+ "integrity": "sha512-n5CHC0hfxOo0jo/3kS4O3KiwhirhZDlx5zZTIuPyIf3Dq0yI/R6R64btx7NYwZe5dI0NjvqzEtnv19m8unwLMg==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -28,9 +28,9 @@
@@ -28,9 +28,9 @@
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.76.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.23.1",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.29.2",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.79.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.25.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.0.0",
"pg": "^8.23.0"
"pg": "^8.23.0"
},
},
"engines": {
"engines": {
@@ -15,69 +15,26 @@
@@ -15,69 +15,26 @@
* audience openvibe.events) only when EVENTS_URL and OV_OAUTH_CLIENT_SECRET are set; otherwise rows
* audience openvibe.events) only when EVENTS_URL and OV_OAUTH_CLIENT_SECRET are set; otherwise rows
* wait in event_outbox and /api/ready reports the relay as off. Payloads never carry a secret.
* wait in event_outbox and /api/ready reports the relay as off. Payloads never carry a secret.
*/
*/
-const { createClient } = require('openvibe-sdk/core');
-const { createServiceTokenClient } = require('openvibe-sdk/auth');
-const { createEventsClient, createPgOutbox } = require('openvibe-sdk/events');
+const { createServiceOutbox } = require('openvibe-sdk/events');
const EVENT_TYPES = ['codes.app.published', 'codes.app.deprecated', 'codes.app.revoked', 'codes.moderation.action'];
const EVENT_TYPES = ['codes.app.published', 'codes.app.deprecated', 'codes.app.revoked', 'codes.moderation.action'];
+/** Codes' wiring of the SDK's shared outbox (plan T1). */
function createCodesOutbox({ db, config, fetchImpl, now, log = console }) {
function createCodesOutbox({ db, config, fetchImpl, now, log = console }) {
- const enabled = Boolean(config.events.url && config.oauth.clientSecret);
- const clientOpts = { baseUrls: { events: config.events.url || 'http://127.0.0.1:4300' }, retries: 0, autoDiscover: false };
- if (fetchImpl) clientOpts.fetch = fetchImpl;
- if (enabled) {
- clientOpts.tokenProvider = createServiceTokenClient({
- tokenUrl: `${config.networkInternalUrl}/oauth/token`, clientId: config.oauth.clientId, clientSecret: config.oauth.clientSecret,
- scope: { 'openvibe.events': 'events.event.publish' }, ...(fetchImpl ? { fetch: fetchImpl } : {}),
- });
- } else {
- clientOpts.getToken = async () => { throw new Error('events relay disabled (EVENTS_URL / OV_OAUTH_CLIENT_SECRET unset)'); };
- }
- const events = createEventsClient(createClient(clientOpts), { source: 'codes' });
- let lastError = null;
- // The PostgreSQL outbox: rows are written in the change's own transaction (enqueue(db, …) joins the ambient
- // transaction); several processes relay one table safely (leases).
- const outbox = createPgOutbox(db, {
- events,
+ return createServiceOutbox({
+ db,
+ source: 'codes',
+ eventsUrl: config.events.url,
+ networkInternalUrl: config.networkInternalUrl,
+ clientId: config.oauth.clientId,
+ clientSecret: config.oauth.clientSecret,
intervalMs: config.events.intervalMs,
intervalMs: config.events.intervalMs,
- now,
- onError: (err) => {
- const msg = err && err.message;
- if (msg !== lastError) log.warn('[Codes] event publish failed (will retry):', msg);
- lastError = msg;
- },
+ log,
+ eventTypes: EVENT_TYPES,
+ autoDiscover: false,
+ ...(fetchImpl ? { fetch: fetchImpl } : {}),
+ ...(now ? { now } : {}),
});
});
-
- /** Inside the caller's transaction. Returns the complete envelope (with its event_id). */
- async function emit(envelope, { traceparent } = {}) {
- if (!EVENT_TYPES.includes(envelope.event_type)) throw new Error(`Codes does not produce ${envelope.event_type}`);
- return await outbox.enqueue(db, envelope, { traceparent });
- }
-
- /**
- * codes.moderation.action (common.moderation-action@1), inside the caller's transaction. actor:
- * the staff member's subject; target: { type, id, owner_subject? }. Never the content.
- */
- async function moderationAction({ action, target, actorSubject, reason = null, details = {} }, { traceparent } = {}) {
- const t = { type: target.type, id: String(target.id).slice(0, 200), owner_subject: target.owner_subject || null };
- return await emit({
- event_type: 'codes.moderation.action',
- actor: actorSubject ? { type: 'user', id: actorSubject } : { type: 'service', id: 'codes' },
- subject: { type: 'moderation_action', id: `${t.type}:${t.id}`.slice(0, 200) },
- visibility: 'internal',
- payload: { action, target: t, actor_subject: actorSubject || null, reason: reason ? String(reason).slice(0, 500) : null, details: details || {} },
- }, { traceparent });
- }
-
- return {
- emit,
- moderationAction,
- outbox,
- enabled,
- start() { if (enabled) outbox.start(); },
- stop: () => outbox.stop(),
- status: async () => ({ enabled, pending: await outbox.pending(), rejected: await outbox.rejected(), last_error: lastError }),
- };
}
}
module.exports = { createCodesOutbox, EVENT_TYPES };
module.exports = { createCodesOutbox, EVENT_TYPES };
@@ -19,11 +19,11 @@ const sdkPkg = require('openvibe-sdk/package.json');
@@ -19,11 +19,11 @@ const sdkPkg = require('openvibe-sdk/package.json');
await check('package.json pins the contracts, SDK and shared tags that are installed', async () => {
await check('package.json pins the contracts, SDK and shared tags that are installed', async () => {
const tag = (dep) => (pkg.dependencies[dep].match(/refs\/tags\/(v[0-9.]+)$/) || [])[1];
const tag = (dep) => (pkg.dependencies[dep].match(/refs\/tags\/(v[0-9.]+)$/) || [])[1];
- assert.strictEqual(tag('openvibe-contracts'), 'v0.76.0');
+ assert.strictEqual(tag('openvibe-contracts'), 'v0.79.0');
assert.strictEqual(tag('openvibe-sdk'), `v${sdkPkg.version}`);
assert.strictEqual(tag('openvibe-sdk'), `v${sdkPkg.version}`);
assert.strictEqual(tag('openvibe-shared'), `v${require('openvibe-shared/package.json').version}`);
assert.strictEqual(tag('openvibe-shared'), `v${require('openvibe-shared/package.json').version}`);
- assert.strictEqual(sdkPkg.version, '0.23.1');
- // The contracts tag v0.76.0 carries the released codes manifest and codes.app-manifest@1.
+ assert.strictEqual(sdkPkg.version, '0.25.0');
+ // The contracts tag v0.79.0 carries the released codes manifest and codes.app-manifest@1.
assert.ok(contracts.services.get('codes') && contracts.services.get('codes').status === 'alpha');
assert.ok(contracts.services.get('codes') && contracts.services.get('codes').status === 'alpha');
assert.ok(contracts.resolve('codes.app-manifest@1'));
assert.ok(contracts.resolve('codes.app-manifest@1'));
});
});
@@ -33,13 +33,13 @@ const sdkPkg = require('openvibe-sdk/package.json');
@@ -33,13 +33,13 @@ const sdkPkg = require('openvibe-sdk/package.json');
const r = await t.get(p);
const r = await t.get(p);
assert.strictEqual(r.status, 200, p);
assert.strictEqual(r.status, 200, p);
assert.ok(r.text.includes(`openvibe-contracts v${contractsVersion}`), `${p} names the installed contracts version`);
assert.ok(r.text.includes(`openvibe-contracts v${contractsVersion}`), `${p} names the installed contracts version`);
- assert.ok(r.text.includes('OpenVibe.Contracts/tree/v0.76.0'), `${p} links the pinned tag`);
- if (contractsVersion !== '0.76.0') assert.ok(r.text.includes('(tag v0.76.0)'), `${p} says the tag differs from the package version`);
+ assert.ok(r.text.includes('OpenVibe.Contracts/tree/v0.79.0'), `${p} links the pinned tag`);
+ if (contractsVersion !== '0.79.0') assert.ok(r.text.includes('(tag v0.79.0)'), `${p} says the tag differs from the package version`);
assert.ok(r.text.includes(`openvibe-sdk v${sdkPkg.version}`), `${p} names the SDK version`);
assert.ok(r.text.includes(`openvibe-sdk v${sdkPkg.version}`), `${p} names the SDK version`);
}
}
const v = (await t.get('/api/v1/docs/versions')).json();
const v = (await t.get('/api/v1/docs/versions')).json();
assert.strictEqual(v.contracts, contractsVersion);
assert.strictEqual(v.contracts, contractsVersion);
- assert.strictEqual(v.contracts_tag, 'v0.76.0');
+ assert.strictEqual(v.contracts_tag, 'v0.79.0');
assert.strictEqual(v.sdk, sdkPkg.version);
assert.strictEqual(v.sdk, sdkPkg.version);
});
});