| … | … | @@ -97,8 +97,17 @@ export class FavelaSource { |
| 97 | 97 | // now pins the shape and not just the name. |
| 98 | 98 | const out = {} |
| 99 | 99 | if (fm.name) out.name = fm.name |
| 100 | // ⛔ RENDERED, because AS2 `summary` is HTML and the bio is markdown. Handed over raw, a bio | |
| 101 | // reading `the **dev** box` reaches Mastodon with the asterisks showing — the same defect the | |
| 102 | // NOTE path was built to avoid ("a heading and a list arrive as paragraphs of literal • | |
| 103 | // characters"), surviving one field over because the profile path was written separately. | |
| 100 | 104 | const bio = rec && rec.body ? String(rec.body).trim() : '' |
| 101 | if (bio) out.summary = bio | |
| 105 | if (bio) { | |
| 106 | let html = '' | |
| 107 | try { html = String(render(bio) || '').trim() } catch { html = '' } | |
| 108 | // A bio that will not render still says something; the plain text is better than nothing. | |
| 109 | out.summary = html || bio | |
| 110 | } | |
| 102 | 111 | // ⚠ An oid, not a URL — the bytes are already in the overlay and are not copied. This mints the |
| 103 | 112 | // URL this bridge serves them at, exactly as an attachment does. |
| 104 | 113 | if (fm.avatar) out.icon = { type: 'Image', url: blobUrl({ host: this.host, base: this.base, cid: fm.avatar }) } |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "ap-bridge", |
| 3 | "version": "0.9.87", | |
| 3 | "version": "0.9.88", | |
| 4 | 4 | "description": "ActivityPub membrane \u2014 a second projection of the self-custody atproto peer", |
| 5 | 5 | "type": "module", |
| 6 | 6 | "main": "index.js", |
| … | … | @@ -166,3 +166,21 @@ test('getProfile projects a banner oid as AS2 image, independently of the avatar |
| 166 | 166 | t.ok(onlyBanner.image, 'banner alone still projects') |
| 167 | 167 | t.absent(onlyBanner.icon, 'and no avatar is invented') |
| 168 | 168 | }) |
| 169 | ||
| 170 | // ⛔ AS2 `summary` is HTML; the bio is markdown. | |
| 171 | test('the summary is rendered, not handed over raw', async (t) => { | |
| 172 | const src = new FavelaSource({ | |
| 173 | peer: { | |
| 174 | async profile () { return { frontmatter: { name: 'ash' }, body: 'the **dev** box' } }, | |
| 175 | async reach () { return { networks: ['fediverse'] } }, | |
| 176 | async ownPosts () { return { rows: [] } } | |
| 177 | }, | |
| 178 | host: 'ap.7rnx.net', | |
| 179 | base: 'https://ap.7rnx.net/ap/users/ashatron', | |
| 180 | actorId: 'https://ap.7rnx.net/ap/users/ashatron/actor', | |
| 181 | followersId: 'https://ap.7rnx.net/ap/users/ashatron/followers' | |
| 182 | }) | |
| 183 | const p = await src.getProfile() | |
| 184 | t.absent(p.summary.includes('**'), '⛔ or the asterisks show on every follower’s screen') | |
| 185 | t.ok(/<strong>|<b>/.test(p.summary), 'markdown became HTML') | |
| 186 | }) |