| … | … | @@ -145,6 +145,64 @@ Always contains `favela`; there is no way to opt out of the local substrate. An |
| 145 | 145 | `roomType` or `verb` yields local-only, and matching is exact: `'PUBLIC'` is not `'public'`, |
| 146 | 146 | because normalising here would let a typo widen an audience. |
| 147 | 147 | |
| 148 | ### `lib/levels.js` |
| 149 | |
| 150 | The mip pyramid. A record's media is one asset containing a ladder of levels, not a file plus a set |
| 151 | of related files — so there is no `variant-of` edge, because the levels were never separate objects. |
| 152 | The pointer carries them; there is no manifest file. |
| 153 | |
| 154 | Every level is a whole file with its own oid, which is what lets this sidestep hyper-overlay's |
| 155 | limit rather than fight it: the overlay addresses a file by a whole-file hash, so a prefix cannot be |
| 156 | authenticated and sparse fetch of one blob is impossible. Fetch the pointer, choose a level, fetch |
| 157 | that 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 | |
| 161 | Chosen against real network ceilings, not powers of two: bsky takes 2,000,000 bytes for a post image |
| 162 | and 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 |
| 172 | how every encoder ladder is expressed. `laddersFor` returns `[]` for anything not `image/*` or |
| 173 | `video/*`. |
| 174 | |
| 175 | #### `planLevels({ mimeType, width, height }) -> level[]` |
| 176 | |
| 177 | Which levels should be generated. Never upscales: a level is a floor, not a guarantee, so an asset |
| 178 | smaller than the lowest level has exactly one level and the pointer must not imply levels it lacks. |
| 179 | The source is always level 0 — the author's own bytes, unaltered. |
| 180 | |
| 181 | #### `scaleTo({ width, height }, edge) -> { width, height }` |
| 182 | |
| 183 | Preserves 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 |
| 188 | fits" is a scan from the front and the base is level 0. |
| 189 | |
| 190 | #### `AUTHOR` · `derivedBy(who)` |
| 191 | |
| 192 | What goes in a level's `by`. A pyramid may be partial, and a level may be derived later by a bridge |
| 193 | or a well-resourced peer — deriving is not authoring, so a peer adding a level must say so or an |
| 194 | author's bytes and somebody else's re-encode become indistinguishable. |
| 195 | |
| 196 | #### `selectLevel(pointer, { maxBytes, maxEdge }) -> { level, reason }` |
| 197 | |
| 198 | The level nearest a destination's ceiling. Bridges select; they do not downsample — encoding happens |
| 199 | once, 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 |
| 202 | ceiling" and "the fetch failed" are different facts and a consumer must be able to tell them apart. |
| 203 | It reports what it picked even on success, because a bridge that quietly serves a smaller picture has |
| 204 | altered what the author published. |
| 205 | |
| 148 | 206 | ### `lib/lint.js` |
| 149 | 207 | |
| 150 | 208 | #### `lint(source, { roomType } = {})` |
| … | … | @@ -155,7 +213,8 @@ reach, because the room sets the ceiling and a file on disk does not say which r |
| 155 | 213 | ### `lib/dialect.js` |
| 156 | 214 | |
| 157 | 215 | The pinned micromark extension set (`MICROMARK`), plus `isHttp`, `isMention`, `isBlob`, `blobOid` |
| 158 | | and the URL/wikilink/hashtag patterns. Import this rather than re-deriving what counts as a mention. |
| 216 | and the `URL_RE` / `WIKILINK_RE` / `HASHTAG_RE` / `BLOB_RE` patterns. Import this rather than |
| 217 | re-deriving what counts as a mention. |
| 159 | 218 | |
| 160 | 219 | ## CLI |
| 161 | 220 | |
| … | … | @@ -191,7 +250,7 @@ to the bridge layer, not under every peer. |
| 191 | 250 | |
| 192 | 251 | ## Status |
| 193 | 252 | |
| 194 | | Working. `npm test` runs 77 tests / 2432 asserts, including the conformance vectors and a fuzzer. |
| 253 | Working. `npm test` runs 85 tests / 2453 asserts, including the conformance vectors and a fuzzer. |
| 195 | 254 | |
| 196 | 255 | Used by `favela-peer` (`record`, `render`, `strip`, `reach`) and `favela-surface` (`render`, `uri`). |
| 197 | 256 | Three other projections still derive their own way and do not import this package — `favela-web`, |