feat(14-01): add fetchMessages() to InboxProvider + both implementations

- Add InboxMessage type (subject + html/text body) and fetchMessages() to
  the InboxProvider interface, ImapProvider, and ExchangeInboxProvider
- IMAP: findBodyParts() walks the MIME tree for first text/html + text/plain
  parts, reusing the connect/lock/search/fetchAll skeleton; marks \Seen
- EWS: new getItemBodySoap() requests item:Body, extracts BodyType via the
  existing extractAttr/extractAll helpers, marks IsRead via markReadSoap
- Net-new spec coverage (imap.provider.spec.ts, exchange-inbox.provider.spec.ts)
  mocking ImapFlow and httpntlm.post (via require.cache stub, since httpntlm
  is loaded with a raw require() that vi.mock cannot intercept)
- fetchPdfAttachments untouched in both providers (D-02); full API suite
  (301 tests) + tsc --noEmit stay green

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 13:09:45 +02:00
parent eb668fd5c8
commit c404954bee
5 changed files with 640 additions and 4 deletions
+15 -2
View File
@@ -1,7 +1,7 @@
import { InboxAttachment, InboxConfig, InboxEmail } from './inbox.types';
import { InboxAttachment, InboxConfig, InboxEmail, InboxMessage } from './inbox.types';
// Re-export types for downstream consumers that import from this module
export type { InboxAttachment, InboxConfig, InboxEmail };
export type { InboxAttachment, InboxConfig, InboxEmail, InboxMessage };
/**
* Abstract inbox provider contract — implemented by ImapProvider and
@@ -29,4 +29,17 @@ export interface InboxProvider {
* @returns { success: true } on success, { success: false, message } on failure
*/
testConnection(config: InboxConfig): Promise<{ success: boolean; message?: string }>;
/**
* Connects to the configured inbox, searches for unread emails (optionally
* filtered by sender), and returns each message's subject + HTML/text body.
*
* Additive method (D-02, Phase 14 inbox extraction) — the seam the
* EmailAlertAdapter (Plan 14-03) consumes. Does NOT affect
* fetchPdfAttachments; DKV is not switched to this method.
*
* @param config Decrypted inbox connection parameters
* @returns Array of messages with subject + body (empty array on error)
*/
fetchMessages(config: InboxConfig): Promise<InboxMessage[]>;
}