openvibers/openvibe.codes Commits
Docs: /docs/events links each event's payload contract (117 published) and marks the rest planned, instead of saying payload schemas do not exist; openvibe-contracts v0.34.2; the per-address rate limit is CODES_RATE_LIMIT_PER_MIN (default 300), raised in tests that fetch every contract page
Changes
@@@ -16,7 +16,7 @@"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.33.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.2","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.1"},@@@ -1023,9 +1023,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.33.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",-- "integrity": "sha512-7q8NUhcPxgb8lxrVdC3pFbiPdjzliJM1mMP/1G5a2PmOr2tJVAXwYa8+a8YgCJvYeMSeMHWijLkIEiZ9IhllOg==",++ "version": "0.34.2",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.2",++ "integrity": "sha512-LV+8rnyfok0c7JsWymmrmo49BGL+nFvvcB75l1Ogx5iVoTw6PmX+jFUbQtnvYchKPmnzu2irfT2VOI49GSro7w==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -27,7 +27,7 @@"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.33.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.2","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.1"},@@@ -123,7 +123,7 @@ function createApp(opts = {}) {app.use('/api', (req, res) => contracts.http.sendProblem(res, 404, 'route.not_found', { detail: 'No such API route', ctx: req.ov }));// ── Pages ───────────────────────────────────────────────-- app.use(rateLimit({ windowMs: 60_000, limit: 300, standardHeaders: true, legacyHeaders: false }));++ app.use(rateLimit({ windowMs: 60_000, limit: Number(process.env.CODES_RATE_LIMIT_PER_MIN) || 300, standardHeaders: true, legacyHeaders: false })); // per address; tests raise itapp.use('/docs', createDocsRoutes(ctx));app.use('/projects', rateLimit({ windowMs: 60_000, limit: 60, standardHeaders: true, legacyHeaders: false, skip: (req) => req.method === 'GET' }), createPortalRoutes(ctx));app.use('/releases', createReleaseActionRoutes(ctx));@@@ -148,13 +148,15 @@ ${c.outputSchema ? html`<dt>Output</dt><dd>${c.outputSchema}</dd>` : ''}});// ── Events ──────────────────────────────────────────────++ // Event types that have a payload contract (events/payloads/<type>.v<major>.json in openvibe-contracts).++ const payloadIds = new Set(require('openvibe-contracts').catalog.filter((c) => String(c.schema || '').startsWith('events/payloads/')).map((c) => c.id));r.get('/events', (req, res) => {page(req, res, {title: 'Event types',crumbs: [{ label: 'Docs', href: '/docs' }, { label: 'Events' }],body: html`<h1>Event types</h1>${versions()}--<p>Every event type a service manifest declares it produces, and who declares they consume it. Events travel as <a href="/docs/contracts/events.event-envelope">events.event-envelope@1</a>; deliveries are signed (<a href="/tools/webhooks">webhook tester</a>). Contracts do not define per-event payload schemas yet.</p>--${table(['Event type', 'Produced by', 'Consumed by'], docs.events.map((e) => [code(e.type), e.producers.join(', '), e.consumers.join(', ') || '—']))}`,++<p>Every event type a service manifest declares it produces, and who declares they consume it. Events travel as <a href="/docs/contracts/events.event-envelope">events.event-envelope@1</a>; deliveries are signed (<a href="/tools/webhooks">webhook tester</a>). Each event's payload has its own contract where one is published (named after the event type, version = the envelope's <code>version</code>).</p>++${table(['Event type', 'Payload', 'Produced by', 'Consumed by'], docs.events.map((e) => [code(e.type), payloadIds.has(e.type) ? html`<a href="/docs/contracts/${e.type}">schema</a>` : html`<span class="muted">planned</span>`, e.producers.join(', '), e.consumers.join(', ') || '—']))}`,});});@@@ -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.33.0');++ assert.strictEqual(tag('openvibe-contracts'), 'v0.34.2');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.5.0');-- // The contracts tag v0.33.0 carries the released codes manifest and codes.app-manifest@1.++ // The contracts tag v0.34.2 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.33.0'), `${p} links the pinned tag`);-- if (contractsVersion !== '0.33.0') assert.ok(r.text.includes('(tag v0.33.0)'), `${p} says the tag differs from the package version`);++ assert.ok(r.text.includes('OpenVibe.Contracts/tree/v0.34.2'), `${p} links the pinned tag`);++ if (contractsVersion !== '0.34.2') assert.ok(r.text.includes('(tag v0.34.2)'), `${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.33.0');++ assert.strictEqual(v.contracts_tag, 'v0.34.2');assert.strictEqual(v.sdk, sdkPkg.version);});@@@ -1,4 +1,6 @@'use strict';++// The docs tests fetch one page per contract (hundreds); the per-address limit would answer 429 part-way.++process.env.CODES_RATE_LIMIT_PER_MIN = process.env.CODES_RATE_LIMIT_PER_MIN || '100000';/*** Boots Codes on a temp database with mocks of Network, Events and Media, captures every log line* (the app's logger AND console), and returns a small HTTP client. Every test file gets its own.
@@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@
"express": "^4.21.2",
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"helmet": "^8.1.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.2",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.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.1"
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"
},
},
@@ -1023,9 +1023,9 @@
@@ -1023,9 +1023,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.33.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
- "integrity": "sha512-7q8NUhcPxgb8lxrVdC3pFbiPdjzliJM1mMP/1G5a2PmOr2tJVAXwYa8+a8YgCJvYeMSeMHWijLkIEiZ9IhllOg==",
+ "version": "0.34.2",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.2",
+ "integrity": "sha512-LV+8rnyfok0c7JsWymmrmo49BGL+nFvvcB75l1Ogx5iVoTw6PmX+jFUbQtnvYchKPmnzu2irfT2VOI49GSro7w==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -27,7 +27,7 @@
@@ -27,7 +27,7 @@
"express": "^4.21.2",
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"helmet": "^8.1.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.2",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.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.1"
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"
},
},
@@ -123,7 +123,7 @@ function createApp(opts = {}) {
@@ -123,7 +123,7 @@ function createApp(opts = {}) {
app.use('/api', (req, res) => contracts.http.sendProblem(res, 404, 'route.not_found', { detail: 'No such API route', ctx: req.ov }));
app.use('/api', (req, res) => contracts.http.sendProblem(res, 404, 'route.not_found', { detail: 'No such API route', ctx: req.ov }));
// ── Pages ───────────────────────────────────────────────
// ── Pages ───────────────────────────────────────────────
- app.use(rateLimit({ windowMs: 60_000, limit: 300, standardHeaders: true, legacyHeaders: false }));
+ app.use(rateLimit({ windowMs: 60_000, limit: Number(process.env.CODES_RATE_LIMIT_PER_MIN) || 300, standardHeaders: true, legacyHeaders: false })); // per address; tests raise it
app.use('/docs', createDocsRoutes(ctx));
app.use('/docs', createDocsRoutes(ctx));
app.use('/projects', rateLimit({ windowMs: 60_000, limit: 60, standardHeaders: true, legacyHeaders: false, skip: (req) => req.method === 'GET' }), createPortalRoutes(ctx));
app.use('/projects', rateLimit({ windowMs: 60_000, limit: 60, standardHeaders: true, legacyHeaders: false, skip: (req) => req.method === 'GET' }), createPortalRoutes(ctx));
app.use('/releases', createReleaseActionRoutes(ctx));
app.use('/releases', createReleaseActionRoutes(ctx));
@@ -148,13 +148,15 @@ ${c.outputSchema ? html`<dt>Output</dt><dd>${c.outputSchema}</dd>` : ''}
@@ -148,13 +148,15 @@ ${c.outputSchema ? html`<dt>Output</dt><dd>${c.outputSchema}</dd>` : ''}
});
});
// ── Events ──────────────────────────────────────────────
// ── Events ──────────────────────────────────────────────
+ // Event types that have a payload contract (events/payloads/<type>.v<major>.json in openvibe-contracts).
+ const payloadIds = new Set(require('openvibe-contracts').catalog.filter((c) => String(c.schema || '').startsWith('events/payloads/')).map((c) => c.id));
r.get('/events', (req, res) => {
r.get('/events', (req, res) => {
page(req, res, {
page(req, res, {
title: 'Event types',
title: 'Event types',
crumbs: [{ label: 'Docs', href: '/docs' }, { label: 'Events' }],
crumbs: [{ label: 'Docs', href: '/docs' }, { label: 'Events' }],
body: html`<h1>Event types</h1>${versions()}
body: html`<h1>Event types</h1>${versions()}
-<p>Every event type a service manifest declares it produces, and who declares they consume it. Events travel as <a href="/docs/contracts/events.event-envelope">events.event-envelope@1</a>; deliveries are signed (<a href="/tools/webhooks">webhook tester</a>). Contracts do not define per-event payload schemas yet.</p>
-${table(['Event type', 'Produced by', 'Consumed by'], docs.events.map((e) => [code(e.type), e.producers.join(', '), e.consumers.join(', ') || '—']))}`,
+<p>Every event type a service manifest declares it produces, and who declares they consume it. Events travel as <a href="/docs/contracts/events.event-envelope">events.event-envelope@1</a>; deliveries are signed (<a href="/tools/webhooks">webhook tester</a>). Each event's payload has its own contract where one is published (named after the event type, version = the envelope's <code>version</code>).</p>
+${table(['Event type', 'Payload', 'Produced by', 'Consumed by'], docs.events.map((e) => [code(e.type), payloadIds.has(e.type) ? html`<a href="/docs/contracts/${e.type}">schema</a>` : html`<span class="muted">planned</span>`, e.producers.join(', '), e.consumers.join(', ') || '—']))}`,
});
});
});
});
@@ -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.33.0');
+ assert.strictEqual(tag('openvibe-contracts'), 'v0.34.2');
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.5.0');
assert.strictEqual(sdkPkg.version, '0.5.0');
- // The contracts tag v0.33.0 carries the released codes manifest and codes.app-manifest@1.
+ // The contracts tag v0.34.2 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.33.0'), `${p} links the pinned tag`);
- if (contractsVersion !== '0.33.0') assert.ok(r.text.includes('(tag v0.33.0)'), `${p} says the tag differs from the package version`);
+ assert.ok(r.text.includes('OpenVibe.Contracts/tree/v0.34.2'), `${p} links the pinned tag`);
+ if (contractsVersion !== '0.34.2') assert.ok(r.text.includes('(tag v0.34.2)'), `${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.33.0');
+ assert.strictEqual(v.contracts_tag, 'v0.34.2');
assert.strictEqual(v.sdk, sdkPkg.version);
assert.strictEqual(v.sdk, sdkPkg.version);
});
});
@@ -1,4 +1,6 @@
@@ -1,4 +1,6 @@
'use strict';
'use strict';
+// The docs tests fetch one page per contract (hundreds); the per-address limit would answer 429 part-way.
+process.env.CODES_RATE_LIMIT_PER_MIN = process.env.CODES_RATE_LIMIT_PER_MIN || '100000';
/**
/**
* Boots Codes on a temp database with mocks of Network, Events and Media, captures every log line
* Boots Codes on a temp database with mocks of Network, Events and Media, captures every log line
* (the app's logger AND console), and returns a small HTTP client. Every test file gets its own.
* (the app's logger AND console), and returns a small HTTP client. Every test file gets its own.