macsync/docs/modules/imail.md

85 lines
3.3 KiB
Markdown

# iMail (`IMailSync`)
## Purpose
Read Mail.app messages via AppleScript / ScriptingBridge and stream them to
the server.
## Direction
Read-only in practice (Mac to server). The `Sender` is implemented but unwired
— see [known-limitations](../known-limitations.md#imail-sender-is-unwired).
## OS surface
Mail.app via ScriptingBridge. The `Package.swift:64-66` linker setting pulls in
`ScriptingBridge.framework`. Reader runs synchronously on a background thread
(`@packages/imail/Sources/IMailSync/SyncManager.swift:90-93`). Requires
Automation > Mail permission.
## Files
- `Reader.swift``Reader.shared`; `fetchMessages(since:)` traverses Mail.app
accounts, mailboxes, and messages. Applies a ±1-minute watermark tolerance
(documented at `SyncManager.swift:40-43`).
- `MIMEParser.swift` — minimal MIME header decoder used by the Reader.
- `APIClient.swift`:
- `syncMail(_:)` -> `POST /client/imail/sync`
(`@packages/imail/Sources/IMailSync/APIClient.swift:86-88`)
- `getStats()` -> `GET /client/imail/stats` (line 102-103)
- `Sender.swift``Sender.send(_:)` drives Mail.app via ScriptingBridge.
- `SendQueueAdapter.swift``IMailSendTransport` + `MailSender` + dispatch
closure. Used by the `SyncManager`'s `SendQueueClient` to drain
`/client/imail/send-queue/pending`.
- `APIClient.swift``getPendingSends()` / `reportSendResult(...)` added
alongside the existing read endpoints.
- `SyncManager.swift` — reads off-main, batches 50; owns a lazy
`SendQueueClient<IMailSendTransport>` started via `didStartSync()` and
stopped via `willStopSync()`.
## Timing
- Read interval: **300s** (`@packages/imail/Sources/IMailSync/SyncManager.swift:61`).
- Outbound `SendQueueClient`: **60s**.
- Batch size: 50.
## Server surface
- Entity tables: existing mail-ingest tables live under `features/imail/`;
outbound table is `icloud.mail_send_queue`
(`src/server/src/entities/mailSendItem/schema.ts`), built on the shared
`sendQueueTableSql` factory.
- Client routes (`src/server/src/surfaces/client/imail.ts`):
- `GET /client/imail/stats`
- `POST /client/imail/sync`
- `GET /client/imail/send-queue/pending`
- `POST /client/imail/send-queue/:id/result`
- Web routes (`src/server/src/surfaces/my/mail.ts`):
- `GET /my/mail/conversations`
- `GET /my/mail/conversations/:id/messages`
- `GET /my/mail/last-sync`
- `POST /my/mail/send` — enqueues a `send_mail` action
- Admin route (`src/server/src/surfaces/admin/mail-send-queue.ts`):
- `POST /admin/mail-send-queue/enqueue`
## Web surface
- Tab: `/mail` (`web/src/App.tsx:59`).
- API helpers: `web/src/api/mail.ts`.
## Known limitations
- **AppleScript / ScriptingBridge fragility.** The reader runs in a detached
`Task` but a hung Mail.app blocks until macOS times the Apple event out.
The send path inherits the same constraint.
- **No folder browser** in `/my/mail`; only conversations and messages by ID.
- **No CC/BCC UI** in the web Compose form yet (the server payload schema and
Sender both accept them; only the web form is missing them).
## Tests (`@packages/imail/Tests/IMailSyncTests/`)
- `MIMEParserTests.swift` — header decoding (hermetic).
- `SenderTests.swift``MailSender` dispatch with a `FakeApplier`: success
path, unknown-action failure, error propagation.
Not covered by tests: Mail.app reading, ScriptingBridge bindings, live send.