openvibers/openvibe.wiki Commits

Wiki articles resolve their links together (one round trip each in series) (#187)

OP OpenVibers committed 12 days ago · d7bbbdecd6 · 3 files, +44 −4

Changes

ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex 1894e96..b68ce81 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -392,10 +392,12 @@ function createPages({ svc, viewers, platform, config, log = console, limits, vi
}
// Infobox values of type page link to that page: resolved before the (synchronous) view renders.
const infoboxLinks = new Map();
++ const infoboxTargets = new Map();
for (const x of v.infobox) {
-- if (x.type !== 'page' || infoboxLinks.has(x.value)) continue;
-- infoboxLinks.set(x.value, await svc.resolveLink(content.parseTarget(x.value, space.slug) || { space: space.slug, slug: '-', title: x.value }, req.actor));
++ if (x.type !== 'page' || infoboxTargets.has(x.value)) continue;
++ infoboxTargets.set(x.value, content.parseTarget(x.value, space.slug) || { space: space.slug, slug: '-', title: x.value });
}
++ await Promise.all([...infoboxTargets].map(async ([value, target]) => infoboxLinks.set(value, await svc.resolveLink(target, req.actor))));
const [bodyHtml, discussion] = await Promise.all([
svc.renderRevision(space, v.revision, req.actor),
v.isPublishedRevision ? await discussionFor(space, page, req.actor) : { state: 'not_public' },
ddiff --git a/server/wiki/service.js b/server/wiki/service.js
iindex 276fb19..7b1344a 100644
---- a/server/wiki/service.js
++++ b/server/wiki/service.js
@@@ -1117,8 +1117,10 @@ function createWikiService({ db, stores, outbox, config, community = null, vip =
*/
async renderRevision(space, rev, actor, { attachments = null, mediaUrl = null } = {}) {
const targets = content.extractLinks(rev.content, space.slug);
-- const resolved = new Map();
-- for (const t of targets) resolved.set(`${t.space}\u0000${t.slug}`, await svc.resolveLink(t, actor));
++ // One lookup per distinct target, all at once: a well-linked article names dozens, and one round trip each
++ // in series made /w/:space/:slug wait on every link before it rendered.
++ const unique = new Map(targets.map((t) => [`${t.space}\u0000${t.slug}`, t]));
++ const resolved = new Map(await Promise.all([...unique].map(async ([key, t]) => [key, await svc.resolveLink(t, actor)])));
const byMedia = new Map((attachments || []).map((a) => [a.mediaId, a]));
const media = mediaUrl ? (id, caption) => {
const a = byMedia.get(id);
ddiff --git a/test/article-links.test.js b/test/article-links.test.js
nnew file mode 100644
iindex 0000000..4db96a3
---- /dev/null
++++ b/test/article-links.test.js
@@@ -0,0 +1,36 @@
++'use strict';
++/**
++ * An article's [[links]] are resolved before it renders (server/wiki/service.js renderRevision): every distinct target
++ * once, together, so a well-linked page does not wait on one database round trip per link. The page still links every
++ * page that exists, whatever the case or spelling, and still names a page that does not.
++ */
++const assert = require('assert');
++const H = require('./helpers');
++
++(async () => {
++ const h = await H.boot();
++ const me = { kind: 'user', subject: H.subject() };
++ try {
++ const space = await h.svc.createSpace({ name: 'Links', slug: 'links' }, me);
++ const make = async (title, body) => {
++ const { page } = await h.svc.createPage(space.id, {
++ title, body: `${body} ${H.LONG}`, summary: `${title} in one line.`,
++ citations: [{ url: 'https://example.org/', title: 'Example', retrievedAt: '2026-10-01' }],
++ }, me);
++ await h.svc.publish(page.id, {}, me);
++ return page;
++ };
++ const beta = await make('Beta', 'Beta page.');
++ const gamma = await make('Gamma', 'Gamma page.');
++ await make('Alpha', 'Alpha links to [[Beta]], [[gamma]] and [[Missing page]].');
++
++ const r = await H.req(h, 'GET', '/w/links/alpha');
++ assert.strictEqual(r.status, 200);
++ assert.ok(r.text.includes(`href="/w/links/${beta.slug}"`), 'the link to Beta resolved');
++ assert.ok(r.text.includes(`href="/w/links/${gamma.slug}"`), 'the lower-case link to Gamma resolved to the same page');
++ assert.ok(r.text.includes('Missing page'), 'a page that does not exist is still named');
++ console.log('article links ok');
++ } finally {
++ await h.stop();
++ }
++})().catch((e) => { console.error(e); process.exit(1); });