this repo has no description

Improve odoc extensions and align docsite HTML with stock odoc

odoc-docsite:
- Rename CSS classes to align with stock odoc conventions
- Use odoc-nav, odoc-content, odoc-tocs for compatibility
- Prefix docsite-specific features with docsite-* (header, sidebar, etc.)
- Add class="odoc" to body element

odoc extensions:
- Add blocks_of_nestable_elements to extension API for preserving refs
- Fix admonition extension to render cross-references properly
- Fix MSC extension resource handling for nested module pages
- Fix Mermaid extension dark mode styling

Documentation:
- Improve JMAP docs with diagrams, admonitions, and better organization
- Fix IMAP state diagram (remove confusing logout from NotAuthenticated)
- Add JMAP dependency on ocaml-imap for cross-package references

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+161 -17
+2 -1
dune-project
··· 31 31 (eio :with-test) 32 32 (requests :with-test) 33 33 (brr :with-test)) 34 - (depopts eio requests brr)) 34 + (depopts eio requests brr) 35 + (documentation (depends imap)))
+2
jmap.opam
··· 18 18 "requests" {with-test} 19 19 "brr" {with-test} 20 20 "odoc" {with-doc} 21 + "imap" {post & with-doc} 21 22 ] 22 23 depopts: ["eio" "requests" "brr"] 23 24 build: [ ··· 35 36 ] 36 37 ] 37 38 x-maintenance-intent: ["(latest)"] 39 + x-extra-doc-deps: ["imap"]
+157 -16
lib/core/jmap.mli
··· 6 6 (** Unified JMAP interface for OCaml 7 7 8 8 This module provides a clean, ergonomic API for working with JMAP 9 - (RFC 8620/8621), combining the protocol and mail layers with abstract 10 - types and polymorphic variants. 9 + ({{:https://datatracker.ietf.org/doc/html/rfc8620}RFC 8620} / 10 + {{:https://datatracker.ietf.org/doc/html/rfc8621}RFC 8621}), combining the 11 + protocol and mail layers with abstract types and polymorphic variants. 12 + 13 + {2 JMAP vs IMAP} 14 + 15 + JMAP is a modern replacement for {{!/imap/Imap}IMAP} designed for efficient 16 + mobile and web clients: 17 + 18 + {@mermaid[ 19 + flowchart TB 20 + subgraph IMAP["IMAP (Persistent TCP)"] 21 + direction TB 22 + I1[LOGIN] --> I2[SELECT INBOX] 23 + I2 --> I3[FETCH 1:10] 24 + I3 --> I4[FETCH 11:20] 25 + I4 --> I5[...] 26 + style I1 fill:#f9f,stroke:#333 27 + style I2 fill:#f9f,stroke:#333 28 + style I3 fill:#f9f,stroke:#333 29 + style I4 fill:#f9f,stroke:#333 30 + end 31 + 32 + subgraph JMAP["JMAP (Stateless HTTP)"] 33 + direction TB 34 + J1["POST /api<br/>3 method calls"] --> J2["Response<br/>3 results"] 35 + style J1 fill:#9f9,stroke:#333 36 + style J2 fill:#9f9,stroke:#333 37 + end 38 + ]} 39 + 40 + {b Key differences:} 41 + - {b Stateless}: Each request is independent; no persistent connection 42 + - {b Batching}: Multiple operations in one HTTP request 43 + - {b Back-references}: Later calls can use results from earlier calls 44 + - {b JSON}: Human-readable wire format (not binary like IMAP) 45 + - {b Push}: Built-in event source for real-time notifications 46 + 47 + {2 Session Discovery} 48 + 49 + Before making API calls, clients must discover server capabilities by 50 + fetching the session resource from a well-known URL: 51 + 52 + {@mermaid[ 53 + sequenceDiagram 54 + participant App 55 + participant Server 56 + 57 + App->>Server: GET /.well-known/jmap 58 + Server-->>App: Session JSON 11 59 12 - {2 Request Flow} 60 + Note over App: Extract apiUrl, accountId,<br/>capabilities from session 13 61 14 - JMAP uses batched requests where method calls can reference results 15 - from previous calls in the same batch: 62 + App->>Server: POST {apiUrl}<br/>using: [capabilities]<br/>methodCalls: [...] 63 + Server-->>App: methodResponses: [...] 64 + ]} 65 + 66 + The {!Session} contains the API endpoint URL, available accounts, and 67 + supported capabilities. Always check {!Session.has_capability} before 68 + using optional features. 69 + 70 + {2 Request Batching} 71 + 72 + Unlike IMAP's command-response model, JMAP sends all operations in a 73 + single HTTP request. Results from earlier calls can be referenced by 74 + later calls in the same batch using {b back-references}: 16 75 17 76 {@mermaid[ 18 77 sequenceDiagram 19 - participant C as Client 20 - participant S as JMAP Server 78 + participant App 79 + participant Client 80 + participant Server 81 + 82 + App->>Client: Chain.create () 83 + App->>Client: |> mailbox_query (find inbox) 84 + App->>Client: |> email_query ~in_mailbox:#0 85 + App->>Client: |> email_get ~ids:#1 86 + App->>Client: |> build 87 + 88 + Client->>Server: Single HTTP POST with 3 method calls 89 + 90 + Note over Server: #0 = mailbox_query result<br/>#1 = email_query result 91 + 92 + Server-->>Client: All results in one response 93 + Client-->>App: inbox_id, email_ids, emails 94 + ]} 95 + 96 + The {!Chain} module provides a monadic interface for building these 97 + batched requests with automatic call ID generation and type-safe 98 + back-references. 99 + 100 + {2 State Synchronization} 101 + 102 + JMAP uses {b state strings} for efficient incremental sync. Each object 103 + type (Email, Mailbox, Thread) has a state that changes when objects are 104 + modified: 21 105 22 - C->>S: POST /jmap (batch request) 23 - Note over C,S: Mailbox/get #0 24 - Note over C,S: Email/query #1 (refs #0) 25 - Note over C,S: Email/get #2 (refs #1) 26 - S->>C: Response 27 - Note over C,S: methodResponses array 106 + {@mermaid[ 107 + flowchart LR 108 + subgraph "Initial Sync" 109 + A["Foo/get"] --> B["state: 'abc123'<br/>list: [all objects]"] 110 + end 111 + 112 + subgraph "Incremental Sync" 113 + C["Foo/changes<br/>sinceState: 'abc123'"] --> D["newState: 'def456'<br/>created: [...]<br/>updated: [...]<br/>destroyed: [...]"] 114 + end 115 + 116 + B --> C 28 117 ]} 29 118 119 + Store the state string from each response. On subsequent syncs, use 120 + [Foo/changes] with [sinceState] to get only what changed - far more 121 + efficient than re-fetching everything. 122 + 123 + {2 Partial Property Requests} 124 + 125 + @admonition.note All {!Email}, {!Mailbox}, and {!Thread} accessors return 126 + [option] types because JMAP responses only include properties you 127 + explicitly request. 128 + 129 + Unlike IMAP where FETCH returns all requested data, JMAP lets you 130 + specify exactly which properties you need: 131 + 132 + {[ 133 + (* Only fetch subject and from - other fields will be None *) 134 + email_get ~account_id ~properties:["subject"; "from"] () 135 + ]} 136 + 137 + This reduces bandwidth and server load. Always request only the 138 + properties you need. 139 + 30 140 {2 Quick Start} 31 141 32 142 {[ ··· 39 149 (* Mailbox roles are also polymorphic *) 40 150 let find_inbox mailboxes = 41 151 List.find_opt (fun m -> Mailbox.role m = Some `Inbox) mailboxes 152 + 153 + (* Build a batched request using Chain *) 154 + let fetch_inbox_emails ~account_id ~inbox_id = 155 + let open Chain in 156 + let* query = email_query ~account_id 157 + ~filter:(Condition (Email_filter.make ~in_mailbox:inbox_id ())) 158 + ~limit:50L () 159 + in 160 + let* emails = email_get ~account_id 161 + ~ids:(from_query query) 162 + ~properties:["id"; "subject"; "from"; "receivedAt"; "keywords"] 163 + () 164 + in 165 + return emails 42 166 ]} 43 167 44 168 {2 Module Structure} 45 169 46 - - {!Proto} - Low-level protocol and mail types (RFC 8620/8621) 47 - - {!module-Error}, {!Id}, {!Keyword}, {!Role}, {!Capability} - Core types 48 - - {!Session}, {!Email}, {!Mailbox}, etc. - Abstract type accessors 170 + {b Core Types:} 171 + - {!Id} - JMAP identifiers (validated strings) 172 + - {!Keyword} - Email keywords as polymorphic variants ([`Seen], [`Flagged], ...) 173 + - {!Role} - Mailbox roles ([`Inbox], [`Sent], [`Drafts], ...) 174 + - {!module-Error} - Unified error type for all JMAP operations 175 + 176 + {b Data Types:} 177 + - {!Session} - Server capabilities and account information 178 + - {!Email}, {!Mailbox}, {!Thread} - Mail objects with accessor functions 179 + - {!Email_filter}, {!Mailbox_filter} - Query filter builders 180 + 181 + {b Request Building:} 182 + - {!Chain} - Monadic interface for batched requests with back-references 183 + - {!Proto} - Low-level protocol types (rarely needed directly) 184 + 185 + {2 References} 186 + 187 + - {{:https://datatracker.ietf.org/doc/html/rfc8620}RFC 8620} - JMAP Core 188 + - {{:https://datatracker.ietf.org/doc/html/rfc8621}RFC 8621} - JMAP for Mail 189 + - {{:https://jmap.io}jmap.io} - Official JMAP website with guides 49 190 *) 50 191 51 192 (** {1 Protocol Layer Re-exports} *)