85 lines
3.3 KiB
Markdown
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.
|