My agentic slop goes here. Not intended for anyone else!
1(** JMAP Thread types and operations.
2
3 This module implements the JMAP Thread data type as specified in RFC 8621
4 Section 3. Threads represent conversations - collections of related Email
5 objects that are grouped together based on standard email threading algorithms
6 (typically using Message-ID, References, and In-Reply-To headers).
7
8 Threads provide a way to organize emails into conversations, making it easier
9 for users to follow email discussions. Thread objects are server-computed and
10 contain the list of email IDs that belong to the conversation.
11
12 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3: Threads
13*)
14
15open Jmap.Methods
16
17(** Thread object representation.
18
19 A Thread object represents a single conversation thread containing one or more
20 related Email objects. Threads are immutable and server-computed based on
21 email headers and threading algorithms.
22
23 The Thread object contains minimal information - just the thread ID and the
24 list of email IDs that belong to the thread. Additional thread information
25 can be obtained by fetching the constituent Email objects.
26
27 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3
28*)
29module Thread : sig
30 (** Immutable thread object type *)
31 type t
32
33 (** Pretty printing interface *)
34 include Jmap_sigs.PRINTABLE with type t := t
35
36 (** JMAP object interface for property selection and object creation *)
37 include Jmap_sigs.JMAP_OBJECT with type t := t and type id_type := string
38
39 (** Get the server-assigned thread identifier.
40 @return Unique thread ID (Some for all persisted threads, None only for unsaved objects) *)
41 val id : t -> Jmap.Id.t option
42
43 (** Get the list of email IDs belonging to this thread.
44 @return List of email IDs in conversation order *)
45 val email_ids : t -> Jmap.Id.t list
46
47 (** Create a new Thread object.
48 @param Jmap.Id.t Server-assigned thread identifier
49 @param email_ids List of email IDs in the thread
50 @return New thread object *)
51 val v : id:Jmap.Id.t -> email_ids:Jmap.Id.t list -> t
52end
53
54
55(** {1 Thread Methods}
56
57 JMAP method argument and response types for Thread operations.
58 Thread objects support query, get, and changes methods but not
59 queryChanges or set (threads are server-computed).
60*)
61
62(** Arguments for Thread/query method.
63
64 Allows querying for Thread objects based on filter criteria.
65 Since Thread objects don't have many properties, filtering is typically
66 done based on the emails they contain rather than thread properties directly.
67
68 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.1> RFC 8621, Section 3.1
69*)
70module Query_args : sig
71 (** Thread/query arguments *)
72 type t
73
74 (** JSON serialization interface *)
75 include Jmap_sigs.JSONABLE with type t := t
76
77 (** JMAP method arguments interface *)
78 include Jmap_sigs.METHOD_ARGS with type t := t and type account_id := string
79
80 (** Get the account ID for the operation.
81 @return Account identifier where threads will be queried *)
82 val account_id : t -> Jmap.Id.t
83
84 (** Validate query arguments according to JMAP method constraints.
85 @param t Query arguments to validate
86 @return Ok () if valid, Error with description if invalid *)
87 val validate : t -> (unit, string) result
88
89 (** Get the method name for these arguments.
90 @return The JMAP method name "Thread/query" *)
91 val method_name : unit -> string
92
93 (** Get the filter condition for thread selection.
94 @return Filter criteria, or None for no filtering *)
95 val filter : t -> Filter.t option
96
97 (** Get the sort criteria for result ordering.
98 @return List of sort comparators, or None for server default *)
99 val sort : t -> Comparator.t list option
100
101 (** Get the starting position for results.
102 @return Zero-based start position, or None for beginning *)
103 val position : t -> int option
104
105 (** Get the anchor thread ID for relative positioning.
106 @return Thread ID to anchor results from, or None *)
107 val anchor : t -> Jmap.Id.t option
108
109 (** Get the offset from the anchor position.
110 @return Number of positions to offset from anchor *)
111 val anchor_offset : t -> int option
112
113 (** Get the maximum number of results to return.
114 @return Result limit, or None for server default *)
115 val limit : t -> Jmap.UInt.t option
116
117 (** Check if total count should be calculated.
118 @return true to calculate total result count *)
119 val calculate_total : t -> bool option
120
121 (** Create Thread/query arguments.
122 @param account_id Account where threads will be queried
123 @param filter Optional filter criteria
124 @param sort Optional sort comparators
125 @param position Optional starting position
126 @param anchor Optional anchor thread ID
127 @param anchor_offset Optional offset from anchor
128 @param limit Optional result limit
129 @param calculate_total Optional flag to calculate totals
130 @return Thread/query arguments object *)
131 val v :
132 account_id:Jmap.Id.t ->
133 ?filter:Filter.t ->
134 ?sort:Comparator.t list ->
135 ?position:int ->
136 ?anchor:Jmap.Id.t ->
137 ?anchor_offset:int ->
138 ?limit:Jmap.UInt.t ->
139 ?calculate_total:bool ->
140 unit -> t
141
142 (** Convert arguments to JSON for JMAP protocol.
143 @param t Thread/query arguments
144 @return JSON representation *)
145 val to_json : t -> Yojson.Safe.t
146end
147
148(** Response for Thread/query method.
149
150 Contains the list of thread IDs matching the query criteria along with
151 metadata about the query results and pagination information.
152
153 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.1> RFC 8621, Section 3.1
154*)
155module Query_response : sig
156 (** Thread/query response *)
157 type t
158
159 (** JSON serialization interface *)
160 include Jmap_sigs.JSONABLE with type t := t
161
162 (** JMAP method response interface *)
163 include Jmap_sigs.METHOD_RESPONSE with type t := t and type account_id := string and type state := string
164
165 (** Get the account ID from the response.
166 @return Account identifier where threads were queried *)
167 val account_id : t -> Jmap.Id.t
168
169 (** Get the query state string for change tracking.
170 @return State string for use in queryChanges *)
171 val query_state : t -> string
172
173 (** Get the state token for synchronization (alias for query_state).
174 @return State token for change tracking *)
175 val state : t -> string option
176
177 (** Check if this response indicates an error condition.
178 @return false (query responses are success responses) *)
179 val is_error : t -> bool
180
181 (** Check if query changes can be calculated.
182 @return true if queryChanges is supported for this query *)
183 val can_calculate_changes : t -> bool
184
185 (** Get the starting position of the results.
186 @return Zero-based position in the complete result set *)
187 val position : t -> int
188
189 (** Get the list of matching thread IDs.
190 @return Ordered list of thread IDs matching the query *)
191 val ids : t -> Jmap.Id.t list
192
193 (** Get the total number of matching threads.
194 @return Total result count if calculateTotal was requested *)
195 val total : t -> Jmap.UInt.t option
196
197 (** Get the limit that was applied to the results.
198 @return Number of results returned, or None if no limit *)
199 val limit : t -> Jmap.UInt.t option
200
201 (** Create Thread/query response.
202 @param account_id Account where threads were queried
203 @param query_state State string for change tracking
204 @param can_calculate_changes Whether queryChanges is supported
205 @param position Starting position of results
206 @param ids List of matching thread IDs
207 @param total Optional total result count
208 @param limit Optional result limit applied
209 @return Thread/query response object *)
210 val v :
211 account_id:Jmap.Id.t ->
212 query_state:string ->
213 can_calculate_changes:bool ->
214 position:int ->
215 ids:Jmap.Id.t list ->
216 ?total:Jmap.UInt.t ->
217 ?limit:Jmap.UInt.t ->
218 unit -> t
219end
220
221(** Arguments for Thread/get method.
222
223 Extends the standard JMAP get pattern for retrieving Thread objects.
224 Since Thread objects are simple, property filtering is rarely needed.
225
226 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.1> RFC 8621, Section 3.1
227*)
228module Get_args : sig
229 (** Thread/get arguments *)
230 type t
231
232 (** JSON serialization interface *)
233 include Jmap_sigs.JSONABLE with type t := t
234
235 (** JMAP method arguments interface *)
236 include Jmap_sigs.METHOD_ARGS with type t := t and type account_id := string
237
238 (** Get the account ID for the operation.
239 @return Account identifier where threads will be retrieved *)
240 val account_id : t -> Jmap.Id.t
241
242 (** Validate get arguments according to JMAP method constraints.
243 @param t Get arguments to validate
244 @return Ok () if valid, Error with description if invalid *)
245 val validate : t -> (unit, string) result
246
247 (** Get the method name for these arguments.
248 @return The JMAP method name "Thread/get" *)
249 val method_name : unit -> string
250
251 (** Get the specific thread IDs to retrieve.
252 @return List of thread IDs, or None to retrieve all threads *)
253 val ids : t -> Jmap.Id.t list option
254
255 (** Get the properties to include in the response.
256 @return List of property names, or None for all properties *)
257 val properties : t -> string list option
258
259 (** Create Thread/get arguments.
260 @param account_id Account where threads are located
261 @param ids Optional list of specific thread IDs to retrieve
262 @param properties Optional list of properties to include
263 @return Thread/get arguments object *)
264 val v :
265 account_id:Jmap.Id.t ->
266 ?ids:Jmap.Id.t list ->
267 ?properties:string list ->
268 unit -> t
269
270 (** Convert arguments to JSON for JMAP protocol.
271 @param t Thread/get arguments
272 @return JSON representation *)
273 val to_json : t -> Yojson.Safe.t
274end
275
276(** Response for Thread/get method.
277
278 Contains the retrieved Thread objects along with standard JMAP response
279 metadata including state string for change tracking.
280
281 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.1> RFC 8621, Section 3.1
282*)
283module Get_response : sig
284 (** Thread/get response *)
285 type t
286
287 (** JSON serialization interface *)
288 include Jmap_sigs.JSONABLE with type t := t
289
290 (** JMAP method response interface *)
291 include Jmap_sigs.METHOD_RESPONSE with type t := t and type account_id := string and type state := string
292
293 (** Get the account ID from the response.
294 @return Account identifier where threads were retrieved *)
295 val account_id : t -> Jmap.Id.t
296
297 (** Get the current state string for change tracking.
298 @return State string for use in Thread/changes *)
299 val state : t -> string
300
301 (** Check if this response indicates an error condition.
302 @return false (get responses are success responses) *)
303 val is_error : t -> bool
304
305 (** Get the list of retrieved Thread objects.
306 @return List of Thread objects that were found *)
307 val list : t -> Thread.t list
308
309 (** Get the list of thread IDs that were not found.
310 @return List of requested IDs that don't exist *)
311 val not_found : t -> Jmap.Id.t list
312
313 (** Create Thread/get response.
314 @param account_id Account where threads were retrieved
315 @param state Current state string
316 @param list Retrieved thread objects
317 @param not_found IDs that were not found
318 @return Thread/get response object *)
319 val v :
320 account_id:Jmap.Id.t ->
321 state:string ->
322 list:Thread.t list ->
323 not_found:Jmap.Id.t list ->
324 unit -> t
325end
326
327(** Arguments for Thread/changes method.
328
329 Used to retrieve changes to Thread objects since a previous state.
330 Thread changes occur when emails are added to or removed from threads,
331 or when threading algorithms recompute thread relationships.
332
333 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.2> RFC 8621, Section 3.2
334*)
335module Changes_args : sig
336 (** Thread/changes arguments *)
337 type t
338
339 (** JSON serialization interface *)
340 include Jmap_sigs.JSONABLE with type t := t
341
342 (** JMAP method arguments interface *)
343 include Jmap_sigs.METHOD_ARGS with type t := t and type account_id := string
344
345 (** Get the account ID for the operation.
346 @return Account identifier where thread changes are tracked *)
347 val account_id : t -> Jmap.Id.t
348
349 (** Validate changes arguments according to JMAP method constraints.
350 @param t Changes arguments to validate
351 @return Ok () if valid, Error with description if invalid *)
352 val validate : t -> (unit, string) result
353
354 (** Get the method name for these arguments.
355 @return The JMAP method name "Thread/changes" *)
356 val method_name : unit -> string
357
358 (** Get the state string from which to calculate changes.
359 @return Previous state string from Thread/get or Thread/changes *)
360 val since_state : t -> string
361
362 (** Get the maximum number of changes to return.
363 @return Change limit, or None for server default *)
364 val max_changes : t -> Jmap.UInt.t option
365
366 (** Create Thread/changes arguments.
367 @param account_id Account where thread changes are tracked
368 @param since_state Previous state string to compare against
369 @param max_changes Optional limit on number of changes returned
370 @return Thread/changes arguments object *)
371 val v :
372 account_id:Jmap.Id.t ->
373 since_state:string ->
374 ?max_changes:Jmap.UInt.t ->
375 unit -> t
376end
377
378(** Response for Thread/changes method.
379
380 Contains lists of thread IDs that were created, updated, or destroyed
381 since the specified state, along with the new state string for tracking
382 future changes.
383
384 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.2> RFC 8621, Section 3.2
385*)
386module Changes_response : sig
387 (** Thread/changes response *)
388 type t
389
390 (** JSON serialization interface *)
391 include Jmap_sigs.JSONABLE with type t := t
392
393 (** JMAP method response interface *)
394 include Jmap_sigs.METHOD_RESPONSE with type t := t and type account_id := string and type state := string
395
396 (** Get the account ID from the response.
397 @return Account identifier where changes occurred *)
398 val account_id : t -> Jmap.Id.t
399
400 (** Get the old state string that was compared against.
401 @return The since_state parameter from the request *)
402 val old_state : t -> string
403
404 (** Get the new current state string.
405 @return Updated state for use in future Thread/changes calls *)
406 val new_state : t -> string
407
408 (** Get the state token for synchronization (alias for new_state).
409 @return State token for change tracking *)
410 val state : t -> string option
411
412 (** Check if this response indicates an error condition.
413 @return false (changes responses are success responses) *)
414 val is_error : t -> bool
415
416 (** Check if more changes are available.
417 @return true if max_changes limit was reached and more changes exist *)
418 val has_more_changes : t -> bool
419
420 (** Get the list of newly created thread IDs.
421 @return Thread IDs that were created since the old state *)
422 val created : t -> Jmap.Id.t list
423
424 (** Get the list of updated thread IDs.
425 @return Thread IDs whose email lists changed since the old state *)
426 val updated : t -> Jmap.Id.t list
427
428 (** Get the list of destroyed thread IDs.
429 @return Thread IDs that were deleted since the old state *)
430 val destroyed : t -> Jmap.Id.t list
431
432 (** Create Thread/changes response.
433 @param account_id Account where changes occurred
434 @param old_state Previous state string
435 @param new_state Current state string
436 @param has_more_changes Whether more changes are available
437 @param created List of created thread IDs
438 @param updated List of updated thread IDs
439 @param destroyed List of destroyed thread IDs
440 @return Thread/changes response object *)
441 val v :
442 account_id:Jmap.Id.t ->
443 old_state:string ->
444 new_state:string ->
445 has_more_changes:bool ->
446 created:Jmap.Id.t list ->
447 updated:Jmap.Id.t list ->
448 destroyed:Jmap.Id.t list ->
449 unit -> t
450end
451
452(** {1 Helper Functions}
453
454 Utility functions for creating common thread-related filter conditions.
455 These are used with Email/query when searching for emails that belong
456 to specific types of threads, since Thread objects themselves don't
457 support query operations.
458*)
459
460(** Create a filter to find threads containing a specific email.
461 @param email_id The email ID to search for in threads
462 @return Filter condition for Email/query to find related emails *)
463val filter_has_email : Jmap.Id.t -> Filter.t
464
465(** Create a filter to find threads containing emails from a sender.
466 @param sender Email address or name to search for in From fields
467 @return Filter condition for finding threads with emails from the sender *)
468val filter_from : string -> Filter.t
469
470(** Create a filter to find threads containing emails to a recipient.
471 @param recipient Email address or name to search for in To/Cc fields
472 @return Filter condition for finding threads with emails to the recipient *)
473val filter_to : string -> Filter.t
474
475(** Create a filter to find threads containing emails with a subject.
476 @param subject Text to search for in email subjects
477 @return Filter condition for finding threads containing the subject text *)
478val filter_subject : string -> Filter.t
479
480(** Create a filter to find threads with emails received before a Date.t.
481 @param Date.t Cutoff Date.t for filtering
482 @return Filter condition for threads with emails before the Date.t *)
483val filter_before : Jmap.Date.t -> Filter.t
484
485(** Create a filter to find threads with emails received after a Date.t.
486 @param Date.t Start Date.t for filtering
487 @return Filter condition for threads with emails after the Date.t *)
488val filter_after : Jmap.Date.t -> Filter.t
489
490(** {1 Advanced Thread Management} *)
491
492(** Conversation reconstruction state for managing complex threading operations.
493
494 Provides stateful thread management including thread merging, splitting,
495 and recalculation using different threading algorithms.
496*)
497module ConversationState : sig
498 (** Opaque conversation state type *)
499 type t
500
501 (** Create new conversation state.
502
503 @param algorithm Threading algorithm to use (default: `HYBRID)
504 @param auto_merge Whether to automatically merge related threads
505 @param subject_threshold Similarity threshold for subject-based merging
506 @return New conversation state *)
507 val create : ?algorithm:[`RFC5256_REFERENCES | `RFC5256_ORDEREDSUBJECT | `HYBRID | `CONVERSATION] -> ?auto_merge:bool -> ?subject_threshold:float -> unit -> t
508
509 (** Add an email to conversation tracking.
510
511 @param t Conversation state
512 @param email_id Email ID to add to tracking
513 @return Updated conversation state *)
514 val add_email : t -> Jmap.Id.t -> t
515
516 (** Remove an email from conversation tracking.
517
518 @param t Conversation state
519 @param email_id ID of email to remove
520 @return Updated conversation state *)
521 val remove_email : t -> Jmap.Id.t -> t
522
523 (** Find which thread contains a specific email.
524
525 @param t Conversation state
526 @param email_id Email ID to search for
527 @return Thread ID if found *)
528 val find_containing_thread : t -> Jmap.Id.t -> Jmap.Id.t option
529
530 (** Get all emails in a specific thread.
531
532 @param t Conversation state
533 @param thread_id Thread ID
534 @return List of email IDs in the thread *)
535 val get_thread_emails : t -> Jmap.Id.t -> Jmap.Id.t list
536
537 (** Get all current thread groups.
538
539 @param t Conversation state
540 @return List of all thread groups *)
541 val get_all_threads : t -> (Jmap.Id.t * Jmap.Id.t list) list
542
543 (** Merge two threads into one conversation.
544
545 @param t Conversation state
546 @param thread1 First thread ID
547 @param thread2 Second thread ID
548 @return Updated conversation state *)
549 val merge_threads : t -> Jmap.Id.t -> Jmap.Id.t -> t
550
551 (** Split a thread at a specific email.
552
553 @param t Conversation state
554 @param thread_id Thread to split
555 @param split_email Email ID where to split
556 @return Updated conversation state *)
557 val split_thread : t -> Jmap.Id.t -> Jmap.Id.t -> t
558
559 (** Recalculate all thread relationships.
560
561 @param t Conversation state
562 @return Updated conversation state *)
563 val recalculate_threads : t -> t
564
565 (** Change threading algorithm and recalculate.
566
567 @param t Conversation state
568 @param algorithm New algorithm to use
569 @return Updated conversation state *)
570 val set_algorithm : t -> [`RFC5256_REFERENCES | `RFC5256_ORDEREDSUBJECT | `HYBRID | `CONVERSATION] -> t
571
572 (** Get conversation statistics.
573
574 @param t Conversation state
575 @return List of statistics about current threads *)
576 val get_stats : t -> [
577 | `ThreadCount of int
578 | `AverageThreadSize of float
579 | `LargestThread of int
580 | `SingletonThreads of int
581 | `MultiEmailThreads of int
582 ] list
583end
584
585(** Normalize a subject line for threading comparison.
586
587 @param subject Subject line to normalize
588 @return Normalized subject string *)
589val normalize_thread_subject : string -> string
590
591(** {1 Property System} *)
592
593(** Thread object property identifiers for selective retrieval.
594
595 Property identifiers for Thread objects as specified in RFC 8621 Section 3.
596 These identifiers are used in Thread/get requests to specify which properties
597 should be returned, enabling efficient partial object retrieval.
598
599 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3
600*)
601module Property : sig
602 (** Thread object property identifier type.
603
604 Polymorphic variant enumeration of all standard properties available
605 on Thread objects. Thread objects have a minimal set of properties
606 since they primarily serve as containers for email ID lists.
607 *)
608 type t = [
609 | `Id (** Server-assigned unique identifier for the thread *)
610 | `EmailIds (** List of email IDs belonging to this thread *)
611 ]
612
613 (** Convert a property to its JMAP protocol string representation.
614
615 @param prop The property to convert
616 @return JMAP protocol string representation *)
617 val to_string : t -> string
618
619 (** Parse a JMAP protocol string into a property variant.
620
621 @param str The protocol string to parse
622 @return Some property if valid, None if unknown *)
623 val of_string : string -> t option
624
625 (** Get all standard thread properties.
626
627 @return Complete list of all defined thread properties *)
628 val all_properties : t list
629
630 (** Convert a list of properties to their string representations.
631
632 @param properties List of property variants
633 @return List of JMAP protocol strings *)
634 val to_string_list : t list -> string list
635
636 (** Parse a list of strings into property variants.
637
638 @param strings List of JMAP protocol strings
639 @return List of parsed property variants (invalid strings ignored) *)
640 val of_string_list : string list -> t list
641end