OverviewFilesCommitsIssuesDocsReleasesPeers

favela-bridge commits / 262042e

0.0.9: the bsky description is stripped, because it is plain text
Measured on the live profile minutes after 0.0.8 shipped: bskys own AppView rendered "the **dev** box" with the asterisks showing. A bsky description is plain text and the bio is markdown, which is the entire premise of favela-markdown; strip() is the same projection every other plain-text destination already uses. Total, like the rest of this module: a bio that will not parse degrades to its own text rather than taking the profile down with it. 8/8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
262042ee46d45b12dc10a86067d7a0d075ccf5bb
by plankatron <fleeky@gmail.com> · 2026-09-09T23:05:29.000Z

Changed files (3) +26 -2

lib/profile.js +18 -1
……@@ -15,6 +15,8 @@
1515// goes. Dropping one field is recoverable; publishing half a sentence as though it were the whole
1616// one is not.
1717
18import { strip } from 'favela-markdown/lib/strip.js'
19
1820// app.bsky.actor.profile: displayName maxGraphemes 64, description maxGraphemes 256.
1921export const MAX_NAME_GRAPHEMES = 64
2022export const MAX_DESCRIPTION_GRAPHEMES = 256
……@@ -27,6 +29,17 @@ export const BSKY_IMAGE_TYPES = ['image/png', 'image/jpeg']
2729
2830const graphemesOf = (s) => [...String(s || '')].length
2931
32// TOTAL, like everything else here: a bio that will not parse degrades to its own text rather than
33// taking the profile down with it.
34function plainText (md) {
35 try {
36 const r = strip(md)
37 return ((r && r.text) || '').trim()
38 } catch {
39 return String(md || '').trim()
40 }
41}
42
3043/**
3144 * planProfile({ name, bio, avatar, banner }) -> { project, record, images, losses }
3245 *
……@@ -49,7 +62,11 @@ export function planProfile ({ name = null, bio = null, avatar = null, banner =
4962 }
5063 }
5164
52 const d = typeof bio === 'string' ? bio.trim() : ''
65 // ⛔ STRIPPED, because a bsky description is PLAIN TEXT. The bio is markdown — that is the whole
66 // premise of favela-markdown — and handing it over raw publishes `the **dev** box` with the
67 // asterisks showing. Measured on the live profile before this: bsky's own AppView rendered them
68 // literally. `strip` is the same projection every other plain-text destination already uses.
69 const d = typeof bio === 'string' ? plainText(bio) : ''
5370 if (d) {
5471 if (graphemesOf(d) > MAX_DESCRIPTION_GRAPHEMES) {
5572 losses.push('description omitted — ' + graphemesOf(d) + ' graphemes against a limit of ' + MAX_DESCRIPTION_GRAPHEMES)
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "favela-bridge",
3 "version": "0.0.8",
3 "version": "0.0.9",
44 "description": "Projects favela records onto the networks a room may reach. One reader, one reach decision, one projection per network.",
55 "type": "module",
66 "main": "index.js",
test/profile.test.js +7 -0
……@@ -65,3 +65,10 @@ test('adding a banner to a profile that had none is a change', (t) => {
6565 const after = { displayName: 'ash', banner: { ref: { $link: 'bafk9' } } }
6666 t.absent(sameProfile(after, before), '⛔ or the first banner would never be written')
6767})
68
69// ⛔ A bsky description is PLAIN TEXT and the bio is markdown.
70test('the description is stripped, not handed over raw', (t) => {
71 const p = planProfile({ bio: 'the **dev** box, and a [link](https://example.com)' })
72 t.absent(p.record.description.includes('**'), '⛔ or the asterisks show on the live profile')
73 t.ok(p.record.description.includes('dev'), 'the words survive')
74})