My agentic slop goes here. Not intended for anyone else!
1(** High-level JMAP Client API.
2
3 This module provides a high-level JMAP client API inspired by the Rust
4 jmap-client library. Features include automatic result reference chaining,
5 comprehensive error handling, and fluent method calls.
6
7 Key features:
8 - Automatic method chaining with result references (no manual call IDs)
9 - Comprehensive error handling with detailed context and retry hints
10 - Fluent builder patterns for complex queries and operations
11 - High-level methods that eliminate manual JSON construction
12 - Production-ready with connection management and resource cleanup
13
14 {b Usage example}:
15 {[
16 let* client = Client.connect ~credentials env "https://jmap.example.org" in
17 let* emails = Client.query_emails client ~filter:(Filter.in_mailbox inbox_id) ~limit:5 in
18 let* mailbox_id = Client.create_mailbox client ~account_id ~name:"Test" () in
19 Client.destroy_email client ~account_id ~email_id
20 ]} *)
21
22(** {1 Client Lifecycle} *)
23
24(** JMAP client with automatic resource management *)
25type t
26
27(** Enhanced authentication methods *)
28type credentials = [
29 | `Basic of string * string (** Basic auth with username and password *)
30 | `Bearer of string (** Bearer token auth *)
31 | `Custom of string * string (** Custom header name and value *)
32 | `Session_cookie of string * string (** Session cookie name and value *)
33]
34
35(** Advanced client configuration *)
36type config = {
37 connect_timeout : float option; (** Connection timeout in seconds (default: 10.0) *)
38 request_timeout : float option; (** Request timeout in seconds (default: 30.0) *)
39 max_concurrent_requests : int option; (** Maximum concurrent requests (default: 10) *)
40 max_request_size : int option; (** Maximum request size in bytes (default: 10MB) *)
41 user_agent : string option; (** User-Agent header value *)
42 retry_attempts : int option; (** Number of automatic retries (default: 3) *)
43 retry_delay : float option; (** Base delay between retries in seconds (default: 1.0) *)
44 enable_push : bool; (** Enable push notifications (default: false) *)
45}
46
47(** Create default client configuration *)
48val default_config : unit -> config
49
50(** Connect to JMAP server.
51
52 This single function handles:
53 - Session discovery via .well-known/jmap
54 - Authentication and capability negotiation
55 - Connection pooling and resource setup
56 - Error handling with detailed diagnostics
57
58 @param credentials Authentication method
59 @param env Eio environment for network operations
60 @param url Base server URL (will auto-discover JMAP endpoint)
61 @param config Optional configuration (uses defaults if not provided)
62 @return Connected client ready for operations *)
63val connect :
64 credentials:credentials ->
65 ?config:config ->
66 < net : 'a Eio.Net.t ; .. > ->
67 string ->
68 (t, Jmap.Error.error) result
69
70(** Get the primary account ID for mail operations.
71 Most clients only need this for email, mailbox, and thread operations. *)
72val primary_account : t -> string
73
74(** Get account ID for specific capability.
75 @param capability JMAP capability URI (e.g., "urn:ietf:params:jmap:mail")
76 @return Account ID supporting that capability, or None if not available *)
77val account_for_capability : t -> string -> string option
78
79(** Check if server supports a specific capability *)
80val has_capability : t -> string -> bool
81
82(** Get server capabilities and limits *)
83val capabilities : t -> (string * Yojson.Safe.t) list
84
85(** Close client and cleanup all resources *)
86val close : t -> unit
87
88(** {1 Email Operations} *)
89
90(** High-level email query with automatic result chaining.
91
92 Combines Email/query and Email/get into single operation with automatic
93 result reference handling. No manual JSON construction required.
94
95 @param client Connected JMAP client
96 @param account_id Account to query (uses primary_account if not specified)
97 @param filter Email filter conditions (optional)
98 @param sort Sort criteria list (optional, defaults to date descending)
99 @param limit Maximum results to return (optional, defaults to 20)
100 @param properties Email properties to fetch (optional, uses smart defaults)
101 @return List of email objects matching criteria *)
102val query_emails :
103 t ->
104 ?account_id:string ->
105 ?filter:Jmap_email.Query.Filter.t ->
106 ?sort:Jmap_email.Query.Sort.t list ->
107 ?limit:int ->
108 ?properties:Jmap_email.Property.t list ->
109 unit ->
110 (Jmap_email.Email.t list, Jmap.Error.error) result
111
112(** Get specific emails by ID with property selection.
113
114 @param client Connected JMAP client
115 @param account_id Account containing the emails
116 @param ids List of email IDs to fetch
117 @param properties Properties to include (optional, uses smart defaults)
118 @return List of email objects (may be fewer than requested if some IDs don't exist) *)
119val get_emails :
120 t ->
121 ?account_id:string ->
122 string list ->
123 ?properties:Jmap_email.Property.t list ->
124 unit ->
125 (Jmap_email.Email.t list, Jmap.Error.error) result
126
127(** Import raw email message into mailboxes.
128
129 @param client Connected JMAP client
130 @param account_id Target account
131 @param raw_message Complete RFC 5322 message as bytes
132 @param mailbox_ids List of mailboxes to place the message in
133 @param keywords Initial keywords/flags (optional)
134 @param received_at Override received timestamp (optional, uses current time)
135 @return Imported email object *)
136val import_email :
137 t ->
138 account_id:string ->
139 raw_message:bytes ->
140 mailbox_ids:string list ->
141 ?keywords:string list ->
142 ?received_at:Jmap.Types.date ->
143 unit ->
144 (Jmap_email.Email.t, Jmap.Error.error) result
145
146(** Destroy email by ID.
147
148 @param client Connected JMAP client
149 @param account_id Account containing the email
150 @param email_id Email to destroy
151 @return Success unit or detailed error *)
152val destroy_email :
153 t ->
154 account_id:string ->
155 email_id:string ->
156 (unit, Jmap.Error.error) result
157
158(** Set email keywords (flags) - replaces all existing keywords.
159
160 @param client Connected JMAP client
161 @param account_id Account containing the email
162 @param email_id Email to modify
163 @param keywords New keyword list (e.g., ["$seen"; "$flagged"])
164 @return Success unit or detailed error *)
165val set_email_keywords :
166 t ->
167 account_id:string ->
168 email_id:string ->
169 keywords:string list ->
170 (unit, Jmap.Error.error) result
171
172(** Set email mailboxes - replaces all existing mailbox assignments.
173
174 @param client Connected JMAP client
175 @param account_id Account containing the email
176 @param email_id Email to modify
177 @param mailbox_ids New mailbox list
178 @return Success unit or detailed error *)
179val set_email_mailboxes :
180 t ->
181 account_id:string ->
182 email_id:string ->
183 mailbox_ids:string list ->
184 (unit, Jmap.Error.error) result
185
186(** {1 Mailbox Operations} *)
187
188(** Query mailboxes with filtering and sorting.
189
190 @param client Connected JMAP client
191 @param account_id Account to query
192 @param filter Mailbox filter conditions (optional)
193 @param sort Sort criteria (optional, defaults to name ascending)
194 @return List of mailbox objects *)
195val query_mailboxes :
196 t ->
197 ?account_id:string ->
198 ?filter:Jmap_email.Mailbox.Filter.t ->
199 ?sort:Jmap_email.Mailbox.Sort.t list ->
200 unit ->
201 (Jmap_email.Mailbox.t list, Jmap.Error.error) result
202
203(** Create new mailbox.
204
205 @param client Connected JMAP client
206 @param account_id Target account
207 @param name Mailbox name (human-readable)
208 @param parent_id Parent mailbox ID for hierarchy (optional)
209 @param role Special mailbox role (optional, e.g., Inbox, Sent)
210 @return ID of newly created mailbox *)
211val create_mailbox :
212 t ->
213 account_id:string ->
214 name:string ->
215 ?parent_id:string ->
216 ?role:Jmap_email.Mailbox.Role.t ->
217 unit ->
218 (string, Jmap.Error.error) result
219
220(** Destroy mailbox.
221
222 @param client Connected JMAP client
223 @param account_id Account containing mailbox
224 @param mailbox_id Mailbox to destroy
225 @param on_destroy_remove_emails If true, delete contained emails; if false, move to Trash (default: false)
226 @return Success unit or detailed error *)
227val destroy_mailbox :
228 t ->
229 account_id:string ->
230 mailbox_id:string ->
231 ?on_destroy_remove_emails:bool ->
232 unit ->
233 (unit, Jmap.Error.error) result
234
235(** {1 Advanced Features} *)
236
237(** Batch request builder for multiple operations with automatic result chaining.
238
239 This provides the foundation for complex multi-method operations while
240 maintaining the automatic result reference system.
241
242 {b Usage example}:
243 {[
244 let batch = Client.batch client in
245 let query_ref = Batch.query_emails batch ~filter ~limit:10 in
246 let get_ref = Batch.get_emails_ref batch query_ref ~properties in
247 let* (emails, _) = Batch.execute batch in
248 process_emails emails
249 ]} *)
250module Batch : sig
251 type batch_builder
252 type 'a batch_operation
253
254 (** Create new batch request builder *)
255 val create : t -> batch_builder
256
257 (** Add email query to batch with automatic result reference *)
258 val query_emails :
259 batch_builder ->
260 ?account_id:string ->
261 ?filter:Jmap_email.Query.Filter.t ->
262 ?sort:Jmap_email.Query.Sort.t list ->
263 ?limit:int ->
264 unit ->
265 string list batch_operation
266
267 (** Add email get operation using result reference from query *)
268 val get_emails_ref :
269 batch_builder ->
270 string list batch_operation ->
271 ?properties:Jmap_email.Property.t list ->
272 unit ->
273 Jmap_email.Email.t list batch_operation
274
275 (** Execute batch request and return results *)
276 val execute : batch_builder -> (unit, Jmap.Error.error) result
277
278 (** Extract results from completed operations *)
279 val result : 'a batch_operation -> ('a, Jmap.Error.error) result
280end
281
282(** {1 Connection and Resource Management} *)
283
284(** Connection statistics for monitoring *)
285type connection_stats = {
286 requests_sent : int;
287 requests_successful : int;
288 requests_failed : int;
289 bytes_sent : int64;
290 bytes_received : int64;
291 connection_reuses : int;
292 average_response_time : float;
293}
294
295(** Get connection statistics for monitoring *)
296val stats : t -> connection_stats
297
298(** Test connection health *)
299val ping : t -> (unit, Jmap.Error.error) result
300
301(** Force connection refresh (useful after network changes) *)
302val refresh_connection : t -> (unit, Jmap.Error.error) result