OverviewFilesCommitsIssuesDocsReleasesPeers

favela-markdown commits / 5bbd95b

README: 0.2.0 shipped a whole module the API section never mentioned
lib/levels.js — the mip pyramid — landed in 0.2.0 on 2026-09-09 and thirteen of its exports were absent from a README whose own commit history says the API section IS the README (462e960, "an API section, in the shape holepunch writes one"). Checked by diffing every `export function|const|class` in lib/ against the file rather than by reading it: IMAGE_LEVELS, VIDEO_LEVELS, laddersFor, planLevels, scaleTo, makePointer, selectLevel, AUTHOR, derivedBy, and the four dialect patterns. Documented with the constraints a caller can break, since each is a design decision rather than a detail: - the ladder edges are real network ceilings, not powers of two — bsky's 2,000,000 for a post image and 1,000,000 for a profile picture, which the table now quotes - planLevels never upscales; a level is a floor, so a small asset has exactly one - makePointer sorts largest first, so the base is level 0 and selection is a scan - `by` distinguishes an author's bytes from somebody else's re-encode - selectLevel returns a reason even on success, and null-with-reason rather than nothing, because "no level under your ceiling" and "the fetch failed" are different facts Also why it is here at all: every level is a whole file with its own oid, which sidesteps hyper-overlay addressing by whole-file hash rather than fighting it. Test numbers re-measured: 77 -> 85 tests, 2432 -> 2453 asserts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5bbd95b2fdfebd9895ca56670040a43d0e973106
by plankatron <fleeky@gmail.com> · 2026-09-20T20:44:19.000Z

Changed files (1) +61 -2

README.md +61 -2
……@@ -145,6 +145,64 @@ Always contains `favela`; there is no way to opt out of the local substrate. An
145145`roomType` or `verb` yields local-only, and matching is exact: `'PUBLIC'` is not `'public'`,
146146because normalising here would let a typo widen an audience.
147147
148### `lib/levels.js`
149
150The mip pyramid. A record's media is one asset containing a ladder of levels, not a file plus a set
151of related files — so there is no `variant-of` edge, because the levels were never separate objects.
152The pointer carries them; there is no manifest file.
153
154Every level is a whole file with its own oid, which is what lets this sidestep hyper-overlay's
155limit rather than fight it: the overlay addresses a file by a whole-file hash, so a prefix cannot be
156authenticated and sparse fetch of one blob is impossible. Fetch the pointer, choose a level, fetch
157that oid — sparseness by level. Cost is the mip cost and is bounded at 4/3 of the base.
158
159#### `IMAGE_LEVELS` · `VIDEO_LEVELS` · `laddersFor(mimeType)`
160
161Chosen against real network ceilings, not powers of two: bsky takes 2,000,000 bytes for a post image
162and 1,000,000 for a profile picture, and the image edges bracket those.
163
164| image | edge | target bytes |
165|---|---|---|
166| `full` | 2000 | 2000000 |
167| `large` | 1280 | 1000000 |
168| `small` | 640 | 300000 |
169| `thumb` | 320 | 80000 |
170
171`VIDEO_LEVELS` is `1080p` / `720p` / `480p` with a bitrate ladder, keyed on height, because that is
172how every encoder ladder is expressed. `laddersFor` returns `[]` for anything not `image/*` or
173`video/*`.
174
175#### `planLevels({ mimeType, width, height }) -> level[]`
176
177Which levels should be generated. Never upscales: a level is a floor, not a guarantee, so an asset
178smaller than the lowest level has exactly one level and the pointer must not imply levels it lacks.
179The source is always level 0 — the author's own bytes, unaltered.
180
181#### `scaleTo({ width, height }, edge) -> { width, height }`
182
183Preserves aspect. Whole pixels, never zero.
184
185#### `makePointer({ mimeType, size, levels }) -> pointer`
186
187`{ mimeType, size, levels: [{ oid, w, h, size, by }] }`, sorted largest first so "the biggest that
188fits" is a scan from the front and the base is level 0.
189
190#### `AUTHOR` · `derivedBy(who)`
191
192What goes in a level's `by`. A pyramid may be partial, and a level may be derived later by a bridge
193or a well-resourced peer — deriving is not authoring, so a peer adding a level must say so or an
194author's bytes and somebody else's re-encode become indistinguishable.
195
196#### `selectLevel(pointer, { maxBytes, maxEdge }) -> { level, reason }`
197
198The level nearest a destination's ceiling. Bridges select; they do not downsample — encoding happens
199once, at authoring, deterministically, instead of per delivery on every bridge.
200
201`level` is `null` when none fits, always with a `reason`: "this asset has no level under your
202ceiling" and "the fetch failed" are different facts and a consumer must be able to tell them apart.
203It reports what it picked even on success, because a bridge that quietly serves a smaller picture has
204altered what the author published.
205
148206### `lib/lint.js`
149207
150208#### `lint(source, { roomType } = {})`
……@@ -155,7 +213,8 @@ reach, because the room sets the ceiling and a file on disk does not say which r
155213### `lib/dialect.js`
156214
157215The pinned micromark extension set (`MICROMARK`), plus `isHttp`, `isMention`, `isBlob`, `blobOid`
158and the URL/wikilink/hashtag patterns. Import this rather than re-deriving what counts as a mention.
216and the `URL_RE` / `WIKILINK_RE` / `HASHTAG_RE` / `BLOB_RE` patterns. Import this rather than
217re-deriving what counts as a mention.
159218
160219## CLI
161220
……@@ -191,7 +250,7 @@ to the bridge layer, not under every peer.
191250
192251## Status
193252
194Working. `npm test` runs 77 tests / 2432 asserts, including the conformance vectors and a fuzzer.
253Working. `npm test` runs 85 tests / 2453 asserts, including the conformance vectors and a fuzzer.
195254
196255Used by `favela-peer` (`record`, `render`, `strip`, `reach`) and `favela-surface` (`render`, `uri`).
197256Three other projections still derive their own way and do not import this package — `favela-web`,