Build / typing hygiene notes: lotusDenoise closeBundle fs work, types/matrix drift vs the SDK, gradual as any cleanup #210

Closed
opened 2026-09-17 23:25:33 -04:00 by jared · 4 comments
Owner

Migrated from LOTUS_TODO.md on 2026-09-17 (the file is now reference-only).

Dependencies / Build / Hygiene

  • Build-time: lotusDenoise does heavy sequential fs in closeBundle; viteStaticCopy has redundant renames — could be streamlined.
  • patch-folds.mjs edits node_modules directly (robust today; patch-package considered but more brittle to folds restructuring — WON'T-DO unless it breaks).
  • types/matrix/ mirrors SDK types instead of importing them — drift risk; spot-fix highest-risk only.
  • contrib/nginx/contrib/caddy examples: headers + try_files already synced with prod; the prod nginx add_header isn't inherited by cache location blocks (pre-existing; SPA entry / still gets all headers).
  • as any casts across src/ — gradual typing cleanup. Keep commits scoped (bisect-friendly). Keep README fork-sync version/logo current.

_Migrated from `LOTUS_TODO.md` on 2026-09-17 (the file is now reference-only)._ ### Dependencies / Build / Hygiene - Build-time: `lotusDenoise` does heavy sequential `fs` in `closeBundle`; `viteStaticCopy` has redundant renames — could be streamlined. - `patch-folds.mjs` edits `node_modules` directly (robust today; `patch-package` considered but more brittle to folds restructuring — WON'T-DO unless it breaks). - `types/matrix/` mirrors SDK types instead of importing them — drift risk; spot-fix highest-risk only. - `contrib/nginx`/`contrib/caddy` examples: headers + `try_files` already synced with prod; the prod nginx `add_header` isn't inherited by cache `location` blocks (pre-existing; SPA entry `/` still gets all headers). - `as any` casts across `src/` — gradual typing cleanup. Keep commits scoped (bisect-friendly). Keep README fork-sync version/logo current. ---
jared added the priority: lowtech-debt labels 2026-09-17 23:25:33 -04:00
Author
Owner

Progress, 2026-09-23:

  • Prod nginx add_header inheritance: fixed (live + matrix 23ad133). Before, only / carried the security headers. Static assets had no nosniff, and /sw.js had no CSP (a service worker takes its CSP from its own script response). The headers now live in /etc/nginx/snippets/cinny-security-headers.conf, included at server level and in the /sw.js, asset and json/html locations. Checked on production: all seven headers on /, a hashed JS asset, /sw.js, /manifest.json, a font and /config.json, and the SW still installs and controls the page with no CSP violations.
  • as any: 163 → 36 (cinny f3722ae8). I removed every cast and restored only the ones tsc needs. 112 were '… !important' as any in lotus-terminal.css.ts, which vanilla-extract accepts without a cast. eslint warnings 49 → 46, and the ratchet is tightened to 46.

Still open here: types/matrix/ drift, the lotusDenoise closeBundle fs work, and the remaining 36 casts. Those 36 are real type gaps (SDK event content, poll types, RoomInput's Slate nodes) and need actual types, not deletion.

Progress, 2026-09-23: - **Prod nginx `add_header` inheritance: fixed (live + matrix `23ad133`).** Before, only `/` carried the security headers. Static assets had no `nosniff`, and `/sw.js` had **no CSP** (a service worker takes its CSP from its own script response). The headers now live in `/etc/nginx/snippets/cinny-security-headers.conf`, included at server level and in the `/sw.js`, asset and json/html locations. Checked on production: all seven headers on `/`, a hashed JS asset, `/sw.js`, `/manifest.json`, a font and `/config.json`, and the SW still installs and controls the page with no CSP violations. - **`as any`: 163 → 36 (cinny `f3722ae8`).** I removed every cast and restored only the ones `tsc` needs. 112 were `'… !important' as any` in `lotus-terminal.css.ts`, which vanilla-extract accepts without a cast. eslint warnings 49 → 46, and the ratchet is tightened to 46. Still open here: `types/matrix/` drift, the `lotusDenoise` `closeBundle` fs work, and the remaining 36 casts. Those 36 are real type gaps (SDK event content, poll types, RoomInput's Slate nodes) and need actual types, not deletion.
Author
Owner

lotusDenoise closeBundle: measured, won't do. The heaviest step, fs.cpSync of the 57 MB DTLN tree, takes 350 ms. Everything else is ~700 KB of flat copies, inside a multi-minute build. Making it async or parallel would add ordering risk (it must run after viteStaticCopy fills dist/public/element-call) for no measurable gain.

`lotusDenoise` `closeBundle`: **measured, won't do.** The heaviest step, `fs.cpSync` of the 57 MB DTLN tree, takes **350 ms**. Everything else is ~700 KB of flat copies, inside a multi-minute build. Making it async or parallel would add ordering risk (it must run after `viteStaticCopy` fills `dist/public/element-call`) for no measurable gain.
Author
Owner

as any: 39 → 23 (e39714a6)

  • Two typed send helpers in utils/room.ts replace 16 casts, and neither helper uses any:
    • sendRoomMessage: the composer's fire-and-forget sends (text, location, voice, files, GIFs)
    • sendRoomEvent: polls, poll responses and poll ends, forwards, reactions, edits
  • sendRoomMessage also swallows the rejected promise. A failed send already shows on the message itself ("Failed to send" with Retry, or the consent prompt), so it no longer appears as an unhandled MatrixError in the console.
  • getAccountData now narrows to keyof AccountDataEvents.
  • ForwardMessageDialog's guard now narrows contentToSend itself. Behaviour is the same.
  • eslint warnings 46 → 36; the ratchet is tightened to 36.

Verified in Chromium: a text message, a quick reaction and an edit all reach the server with the right content, and a consent-blocked send no longer logs an unhandled error. All 1,219 unit tests pass.

What's left (23) is mostly account-data and state-event types the SDK doesn't know about (io.lotus.*, im.ponies.*), plus poll content parsing and Slate nodes.

## `as any`: 39 → 23 (`e39714a6`) - **Two typed send helpers in `utils/room.ts`** replace 16 casts, and neither helper uses `any`: - `sendRoomMessage`: the composer's fire-and-forget sends (text, location, voice, files, GIFs) - `sendRoomEvent`: polls, poll responses and poll ends, forwards, reactions, edits - `sendRoomMessage` also swallows the rejected promise. A failed send already shows on the message itself ("Failed to send" with Retry, or the consent prompt), so it no longer appears as an unhandled `MatrixError` in the console. - `getAccountData` now narrows to `keyof AccountDataEvents`. - ForwardMessageDialog's guard now narrows `contentToSend` itself. Behaviour is the same. - eslint warnings 46 → 36; the ratchet is tightened to 36. Verified in Chromium: a text message, a quick reaction and an edit all reach the server with the right content, and a consent-blocked send no longer logs an unhandled error. All 1,219 unit tests pass. What's left (23) is mostly account-data and state-event types the SDK doesn't know about (`io.lotus.*`, `im.ponies.*`), plus poll content parsing and Slate nodes.
jared closed this issue 2026-10-25 22:48:05 -04:00
Author
Owner

Done.

  • as any cleanup (#468): the untyped secretStorageKeys.js is now typed against the SDK's CryptoCallbacks. Its cast had hidden an undefined-vs-null mismatch. The 14 casts left are single, documented boundary casts.
  • Denoise build step (#470):
    • Size: the DTLN copy now leaves out the threaded LiteRT build (it can't load without COOP/COEP), source maps and .d.ts files. That's 57 → 48 MB per web deploy and desktop installer. Verified that DTLN still reaches ready and runs.
    • Speed: the copy itself takes about 0.5 s, so it isn't worth restructuring.
  • viteStaticCopy renames: not redundant. v4 keeps source paths, so each stripBase is needed.
  • patch-folds.mjs: stays as noted (won't do unless it breaks).
  • types/matrix drift: no breakage found. The typed boundaries cover the risky spots.

🤖 Generated with Claude Code

**Done.** - **`as any` cleanup (#468):** the untyped `secretStorageKeys.js` is now typed against the SDK's `CryptoCallbacks`. Its cast had hidden an `undefined`-vs-`null` mismatch. The 14 casts left are single, documented boundary casts. - **Denoise build step (#470):** - **Size:** the DTLN copy now leaves out the threaded LiteRT build (it can't load without COOP/COEP), source maps and `.d.ts` files. That's **57 → 48 MB** per web deploy and desktop installer. Verified that DTLN still reaches ready and runs. - **Speed:** the copy itself takes about 0.5 s, so it isn't worth restructuring. - **`viteStaticCopy` renames:** not redundant. v4 keeps source paths, so each `stripBase` is needed. - **`patch-folds.mjs`:** stays as noted (won't do unless it breaks). - **`types/matrix` drift:** no breakage found. The typed boundaries cover the risky spots. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#210