Group six more lib clusters, and split the mock server #374

Closed
opened 2026-09-16 06:22:51 +00:00 by jcoffey-dev · 0 comments
Owner

Two refactors, one theme, no behavior change. Separate commits — review them independently.

1. web/src/lib: 66 flat modules → 42

Folder Modules
lib/mailbox/ archiveDate, emptyFolder, folderMove, labelTree, mailboxName, mailboxRoute
lib/sieve/ sieve, sieveApply, sieveFolders
lib/input/ keyboard, swipe, touch, listSelection, dropUpload
lib/notify/ notify, webpush, webpushEnable
lib/sw/ swCache, swFacts, staleBuild
lib/text/ html, markdown, text, emlName

Four modules are not where their names suggest. I read each file rather than trusting the filename, having already been caught out by archiveDate last time:

  • appFolder is not a mailbox. It is the ihasmail folder in JMAP Files, holding signature images and synced settings. Stays flat.
  • format contains no text formatting. It re-exports the date and clock formatters, so it belongs with dates/datetime, not text/.
  • preview is the file viewer deciding what it can show without downloading; source is where to point someone asking for this instance's AGPL source. Neither is text.
  • notify is not Web Push. It is the tab title, favicon badge and new-mail sound — which is why it sits beside webpush rather than under sw/.

threadScroll also stays flat: it decides where a conversation opens, which is view state, not a gesture, and input/ is only honest if everything in it interprets something the reader did.

2. server/src/mock/index.ts: 1,545 lines → seven files

It already carried /* ---------- data ---------- */ markers, so the seams were drawn; this turns them into files.

File Lines Holds
mock/config.ts 51 env constants, account, state
mock/data.ts 410 fixtures and their builders
mock/engine.ts 442 generic JMAP machinery — get/set, filters, patches, refs, limits
mock/handlers.ts 455 the Method/name dispatch table
mock/events.ts 26 SSE fan-out, Email/changes ring buffer
mock/auth.ts 11 checkOtp
mock/index.ts 195 HTTP routing, session document, listen

Two things could not simply move:

  • counter and vacation were module-level lets written from both the fixture builders and the handlers. A module can export a let and importers see it update, but they cannot assign to it — so both became containers, seq.counter and vacationBox.current. Seven call sites.
  • recordEmailChange, broadcast, sseClients, checkOtp sat in the HTTP section, but the handlers call them and index.ts imports the handlers. Leaving them put is a cycle, so they became events.ts and auth.ts rather than being dragged into data.ts, which is fixtures.

account is still exported from index.tsaccount.test.ts and login-guard.test.ts reach for mock.account and mock.server.

Translations: adds none. No user-visible strings in either commit.

Testing: typecheck exit 0, npm test exit 0 (135 web files / 1,292 tests, 228 server), build exit 0. The mock was also run, not just compiled: it boots and listens, /.well-known/jmap returns a session, and a POST to /jmap/ answers Mailbox/get with the nine seeded folders and Email/query with the seeded messages — which exercises handlers, engine and data together.

Merged 2026-09-15 as coffey-labs/ihasmail@d0b13272f3

Rebuilt from: git history, session transcript.

Two refactors, one theme, no behavior change. Separate commits — review them independently. ## 1. `web/src/lib`: 66 flat modules → 42 | Folder | Modules | | --- | --- | | `lib/mailbox/` | `archiveDate`, `emptyFolder`, `folderMove`, `labelTree`, `mailboxName`, `mailboxRoute` | | `lib/sieve/` | `sieve`, `sieveApply`, `sieveFolders` | | `lib/input/` | `keyboard`, `swipe`, `touch`, `listSelection`, `dropUpload` | | `lib/notify/` | `notify`, `webpush`, `webpushEnable` | | `lib/sw/` | `swCache`, `swFacts`, `staleBuild` | | `lib/text/` | `html`, `markdown`, `text`, `emlName` | **Four modules are not where their names suggest.** I read each file rather than trusting the filename, having already been caught out by `archiveDate` last time: - **`appFolder` is not a mailbox.** It is the `ihasmail` folder in JMAP *Files*, holding signature images and synced settings. Stays flat. - **`format` contains no text formatting.** It re-exports the date and clock formatters, so it belongs with `dates`/`datetime`, not `text/`. - **`preview` is the file viewer** deciding what it can show without downloading; **`source`** is where to point someone asking for this instance's AGPL source. Neither is text. - **`notify` is not Web Push.** It is the tab title, favicon badge and new-mail sound — which is why it sits beside `webpush` rather than under `sw/`. `threadScroll` also stays flat: it decides where a conversation opens, which is view state, not a gesture, and `input/` is only honest if everything in it interprets something the reader did. ## 2. `server/src/mock/index.ts`: 1,545 lines → seven files It already carried `/* ---------- data ---------- */` markers, so the seams were drawn; this turns them into files. | File | Lines | Holds | | --- | --- | --- | | `mock/config.ts` | 51 | env constants, `account`, `state` | | `mock/data.ts` | 410 | fixtures and their builders | | `mock/engine.ts` | 442 | generic JMAP machinery — get/set, filters, patches, refs, limits | | `mock/handlers.ts` | 455 | the `Method/name` dispatch table | | `mock/events.ts` | 26 | SSE fan-out, `Email/changes` ring buffer | | `mock/auth.ts` | 11 | `checkOtp` | | `mock/index.ts` | 195 | HTTP routing, session document, listen | **Two things could not simply move:** - **`counter` and `vacation`** were module-level `let`s written from both the fixture builders and the handlers. A module can export a `let` and importers see it update, but they cannot assign to it — so both became containers, `seq.counter` and `vacationBox.current`. Seven call sites. - **`recordEmailChange`, `broadcast`, `sseClients`, `checkOtp`** sat in the HTTP section, but the handlers call them and `index.ts` imports the handlers. Leaving them put is a cycle, so they became `events.ts` and `auth.ts` rather than being dragged into `data.ts`, which is fixtures. `account` is still exported from `index.ts` — `account.test.ts` and `login-guard.test.ts` reach for `mock.account` and `mock.server`. **Translations:** adds none. No user-visible strings in either commit. **Testing:** `typecheck` exit 0, `npm test` exit 0 (135 web files / 1,292 tests, 228 server), `build` exit 0. The mock was also **run**, not just compiled: it boots and listens, `/.well-known/jmap` returns a session, and a POST to `/jmap/` answers `Mailbox/get` with the nine seeded folders and `Email/query` with the seeded messages — which exercises `handlers`, `engine` and `data` together. **Merged** 2026-09-15 as coffey-labs/ihasmail@d0b13272f3b1 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.