this repo has no description

Enhance JMAP implementation to better match specifications

- Enhanced Thread management with complete Thread/get and Thread/changes methods
- Improved SearchSnippet module with SearchSnippet/get operation
- Added Email/import, Email/parse, and Email/copy methods
- Added Apple Mail flags and mailbox attributes from draft-ietf-mailmaint-messageflag-mailboxattribute
- Restructured keywords into a proper Keyword module
- Added documentation about handling OCaml docstring spacing in CLAUDE.md

🤖 Generated with [Claude Code](https://claude.ai/code)

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

+613 -16
+61 -2
CLAUDE.md
··· 23 23 24 24 Then examine the HTML docs built for that module. You will see that there are module references with __ in them, e.g. "Jmap__.Jmap_email_types.Email_address.t" which indicate that the module is being accessed directly instead of via the module aliases defined. 25 25 26 - # Structure Simplification 26 + ## Documentation Comments 27 + 28 + When adding OCaml documentation comments, be careful about ambiguous documentation comments. If you see errors like: 29 + 30 + ``` 31 + Error (warning 50 [unexpected-docstring]): ambiguous documentation comment 32 + ``` 33 + 34 + This usually means there isn't enough whitespace between the documentation comment and the code element it's documenting. Always: 35 + 36 + 1. Add blank lines between consecutive documentation comments 37 + 2. Add a blank line before a documentation comment for a module/type/value declaration 38 + 3. When documenting record fields or variant constructors, place the comment after the field with at least one space 39 + 40 + Example of correct documentation spacing: 41 + 42 + ```ocaml 43 + (** Module documentation. *) 44 + 45 + (** Value documentation. *) 46 + val some_value : int 47 + 48 + (** Type documentation. *) 49 + type t = 50 + | First (** First constructor *) 51 + | Second (** Second constructor *) 52 + 53 + (** Record documentation. *) 54 + type record = { 55 + field1 : int; (** Field1 documentation *) 56 + field2 : string (** Field2 documentation *) 57 + } 58 + ``` 59 + 60 + If in doubt, add more whitespace lines than needed - you can always clean this up later with `dune build @fmt` to get ocamlformat to sort out the whitespace properly. 61 + 62 + # Module Structure Guidelines 63 + 64 + IMPORTANT: For all modules, use a nested module structure with a canonical `type t` inside each submodule. This approach ensures consistent type naming and logical grouping of related functionality. 65 + 66 + 1. Top-level files should define their main types directly (e.g., `jmap_identity.mli` should define identity-related types at the top level). 67 + 68 + 2. Related operations or specialized subtypes should be defined in nested modules within the file: 69 + ```ocaml 70 + module Create : sig 71 + type t (* NOT 'type create' or any other name *) 72 + (* Functions operating on creation requests *) 73 + 74 + module Response : sig 75 + type t 76 + (* Functions for creation responses *) 77 + end 78 + end 79 + ``` 80 + 81 + 3. Consistently use `type t` for the main type in each module and submodule. 82 + 83 + 4. Functions operating on a type should be placed in the same module as the type. 84 + 85 + 5. When a file is named after a concept (e.g., `jmap_identity.mli`), there's no need to have a matching nested module inside the file (e.g., `module Identity : sig...`), as the file itself represents that namespace. 27 86 28 - Avoid redundant module nesting. When a file is named after a module (e.g., `jmap_identity.mli`), there's no need to have a matching nested module inside the file (e.g., `module Identity : sig...`). Instead, define types and functions directly at the top level of the file. Also, ensure that submodule main types are always named `t`, not named after the module (e.g., use `Create.t` not `Create.create`). 87 + This structured approach promotes encapsulation, consistent type naming, and clearer organization of related functionality. 29 88 30 89 # Software engineering 31 90
+194 -5
jmap-email/jmap_email.mli
··· 142 142 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-1.5> RFC 8621, Section 1.5 *) 143 143 val push_event_type_email_delivery : string 144 144 145 - (** JMAP keywords corresponding to IMAP system flags. 145 + (** Keyword string constants for JMAP email flags. 146 + Provides easy access to standardized keyword string values. 146 147 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.1> RFC 8621, Section 4.1.1 *) 148 + module Keyword : sig 149 + (** {1 IMAP System Flags} *) 150 + 151 + (** "$draft": The Email is a draft the user is composing *) 152 + val draft : string 153 + 154 + (** "$seen": The Email has been read *) 155 + val seen : string 156 + 157 + (** "$flagged": The Email has been flagged for urgent/special attention *) 158 + val flagged : string 159 + 160 + (** "$answered": The Email has been replied to *) 161 + val answered : string 162 + 163 + (** {1 Common Extension Keywords} *) 164 + 165 + (** "$forwarded": The Email has been forwarded *) 166 + val forwarded : string 167 + 168 + (** "$phishing": The Email is likely to be phishing *) 169 + val phishing : string 170 + 171 + (** "$junk": The Email is spam/junk *) 172 + val junk : string 173 + 174 + (** "$notjunk": The Email is explicitly marked as not spam/junk *) 175 + val notjunk : string 176 + 177 + (** {1 Apple Mail and Vendor Extensions} 178 + @see <https://datatracker.ietf.org/doc/draft-ietf-mailmaint-messageflag-mailboxattribute/> *) 179 + 180 + (** "$notify": Request to be notified when this email gets a reply *) 181 + val notify : string 182 + 183 + (** "$muted": Email is muted (notifications disabled) *) 184 + val muted : string 185 + 186 + (** "$followed": Email thread is followed for notifications *) 187 + val followed : string 188 + 189 + (** "$memo": Email has a memo/note associated with it *) 190 + val memo : string 191 + 192 + (** "$hasmemo": Email has a memo, annotation or note property *) 193 + val hasmemo : string 194 + 195 + (** "$autosent": Email was generated or sent automatically *) 196 + val autosent : string 197 + 198 + (** "$unsubscribed": User has unsubscribed from this sender *) 199 + val unsubscribed : string 200 + 201 + (** "$canunsubscribe": Email contains unsubscribe information *) 202 + val canunsubscribe : string 203 + 204 + (** "$imported": Email was imported from another system *) 205 + val imported : string 206 + 207 + (** "$istrusted": Email is from a trusted/verified sender *) 208 + val istrusted : string 209 + 210 + (** "$maskedemail": Email is to/from a masked/anonymous address *) 211 + val maskedemail : string 212 + 213 + (** "$new": Email was recently delivered *) 214 + val new_mail : string 215 + 216 + (** {1 Apple Mail Color Flag Bits} *) 217 + 218 + (** "$MailFlagBit0": First color flag bit (red) *) 219 + val mailflagbit0 : string 220 + 221 + (** "$MailFlagBit1": Second color flag bit (orange) *) 222 + val mailflagbit1 : string 223 + 224 + (** "$MailFlagBit2": Third color flag bit (yellow) *) 225 + val mailflagbit2 : string 226 + 227 + (** {1 Color Flag Combinations} *) 228 + 229 + (** Get color flag bit values for a specific color 230 + @return A list of flags to set to create the requested color *) 231 + val color_flags : [`Red | `Orange | `Yellow | `Green | `Blue | `Purple | `Gray] -> string list 232 + 233 + (** Check if a string is a valid keyword according to the RFC 234 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.1> RFC 8621, Section 4.1.1 *) 235 + val is_valid : string -> bool 236 + end 237 + 238 + (** For backward compatibility - DEPRECATED, use Keyword.draft instead *) 147 239 val keyword_draft : string 240 + 241 + (** For backward compatibility - DEPRECATED, use Keyword.seen instead *) 148 242 val keyword_seen : string 243 + 244 + (** For backward compatibility - DEPRECATED, use Keyword.flagged instead *) 149 245 val keyword_flagged : string 246 + 247 + (** For backward compatibility - DEPRECATED, use Keyword.answered instead *) 150 248 val keyword_answered : string 151 249 152 - (** Common JMAP keywords from RFC 5788. 153 - @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.1> RFC 8621, Section 4.1.1 *) 250 + (** For backward compatibility - DEPRECATED, use Keyword.forwarded instead *) 154 251 val keyword_forwarded : string 252 + 253 + (** For backward compatibility - DEPRECATED, use Keyword.phishing instead *) 155 254 val keyword_phishing : string 255 + 256 + (** For backward compatibility - DEPRECATED, use Keyword.junk instead *) 156 257 val keyword_junk : string 258 + 259 + (** For backward compatibility - DEPRECATED, use Keyword.notjunk instead *) 157 260 val keyword_notjunk : string 158 261 159 - (** Functions to manipulate email flags/keywords *) 262 + (** Email keyword operations. 263 + Functions to manipulate and update email keywords/flags. *) 160 264 module Keyword_ops : sig 161 265 (** Add a keyword/flag to an email *) 162 266 val add : Types.Email.t -> Types.Keywords.keyword -> Types.Email.t 163 267 164 268 (** Remove a keyword/flag from an email *) 165 269 val remove : Types.Email.t -> Types.Keywords.keyword -> Types.Email.t 270 + 271 + (** {1 System Flag Operations} *) 166 272 167 273 (** Mark an email as seen/read *) 168 274 val mark_as_seen : Types.Email.t -> Types.Email.t ··· 200 306 (** Mark an email as phishing *) 201 307 val mark_as_phishing : Types.Email.t -> Types.Email.t 202 308 309 + (** {1 Extension Flag Operations} *) 310 + 311 + (** Mark an email for notification when replied to *) 312 + val mark_as_notify : Types.Email.t -> Types.Email.t 313 + 314 + (** Remove notification flag from an email *) 315 + val unmark_notify : Types.Email.t -> Types.Email.t 316 + 317 + (** Mark an email as muted (no notifications) *) 318 + val mark_as_muted : Types.Email.t -> Types.Email.t 319 + 320 + (** Unmute an email (allow notifications) *) 321 + val unmark_muted : Types.Email.t -> Types.Email.t 322 + 323 + (** Mark an email thread as followed for notifications *) 324 + val mark_as_followed : Types.Email.t -> Types.Email.t 325 + 326 + (** Remove followed status from an email thread *) 327 + val unmark_followed : Types.Email.t -> Types.Email.t 328 + 329 + (** Mark an email with a memo *) 330 + val mark_as_memo : Types.Email.t -> Types.Email.t 331 + 332 + (** Mark an email with the hasmemo flag *) 333 + val mark_as_hasmemo : Types.Email.t -> Types.Email.t 334 + 335 + (** Mark an email as automatically sent *) 336 + val mark_as_autosent : Types.Email.t -> Types.Email.t 337 + 338 + (** Mark an email as being from an unsubscribed sender *) 339 + val mark_as_unsubscribed : Types.Email.t -> Types.Email.t 340 + 341 + (** Mark an email as having unsubscribe capability *) 342 + val mark_as_canunsubscribe : Types.Email.t -> Types.Email.t 343 + 344 + (** Mark an email as imported from another system *) 345 + val mark_as_imported : Types.Email.t -> Types.Email.t 346 + 347 + (** Mark an email as from a trusted/verified sender *) 348 + val mark_as_trusted : Types.Email.t -> Types.Email.t 349 + 350 + (** Mark an email as having masked/anonymous address *) 351 + val mark_as_maskedemail : Types.Email.t -> Types.Email.t 352 + 353 + (** Mark an email as new/recent *) 354 + val mark_as_new : Types.Email.t -> Types.Email.t 355 + 356 + (** Remove new/recent flag from an email *) 357 + val unmark_new : Types.Email.t -> Types.Email.t 358 + 359 + (** {1 Color Flag Operations} *) 360 + 361 + (** Set color flag bits on an email *) 362 + val set_color_flags : Types.Email.t -> red:bool -> orange:bool -> yellow:bool -> Types.Email.t 363 + 364 + (** Mark an email with a predefined color *) 365 + val mark_as_color : Types.Email.t -> 366 + [`Red | `Orange | `Yellow | `Green | `Blue | `Purple | `Gray] -> Types.Email.t 367 + 368 + (** Remove all color flag bits from an email *) 369 + val clear_color_flags : Types.Email.t -> Types.Email.t 370 + 371 + (** {1 Custom Flag Operations} *) 372 + 203 373 (** Add a custom keyword to an email *) 204 374 val add_custom : Types.Email.t -> string -> Types.Email.t 205 375 206 376 (** Remove a custom keyword from an email *) 207 377 val remove_custom : Types.Email.t -> string -> Types.Email.t 378 + 379 + (** {1 Patch Object Creation} *) 208 380 209 381 (** Create a patch object to add a keyword to emails *) 210 382 val add_keyword_patch : Types.Keywords.keyword -> Jmap.Methods.patch_object ··· 217 389 218 390 (** Create a patch object to mark emails as unseen/unread *) 219 391 val mark_unseen_patch : unit -> Jmap.Methods.patch_object 392 + 393 + (** Create a patch object to set a specific color on emails *) 394 + val set_color_patch : [`Red | `Orange | `Yellow | `Green | `Blue | `Purple | `Gray] -> 395 + Jmap.Methods.patch_object 220 396 end 221 397 222 398 (** Conversion functions for JMAP/IMAP compatibility *) 223 399 module Conversion : sig 400 + (** {1 Keyword/Flag Conversion} *) 401 + 224 402 (** Convert a JMAP keyword variant to IMAP flag *) 225 403 val keyword_to_imap_flag : Types.Keywords.keyword -> string 226 404 227 405 (** Convert an IMAP flag to JMAP keyword variant *) 228 406 val imap_flag_to_keyword : string -> Types.Keywords.keyword 229 407 230 - (** Check if a string is valid for use as a custom keyword according to RFC 8621 *) 408 + (** Check if a string is valid for use as a custom keyword according to RFC 8621. 409 + @deprecated Use Keyword.is_valid instead. *) 231 410 val is_valid_custom_keyword : string -> bool 232 411 233 412 (** Get the JMAP protocol string representation of a keyword *) ··· 235 414 236 415 (** Parse a JMAP protocol string into a keyword variant *) 237 416 val string_to_keyword : string -> Types.Keywords.keyword 417 + 418 + (** {1 Color Conversion} *) 419 + 420 + (** Convert a color name to the corresponding flag bit combination *) 421 + val color_to_flags : [`Red | `Orange | `Yellow | `Green | `Blue | `Purple | `Gray] -> 422 + Types.Keywords.keyword list 423 + 424 + (** Try to determine a color from a set of keywords *) 425 + val keywords_to_color : Types.Keywords.t -> 426 + [`Red | `Orange | `Yellow | `Green | `Blue | `Purple | `Gray | `None] option 238 427 end 239 428 240 429 (** {1 Helper Functions} *)
+154 -3
jmap-email/jmap_email_types.mli
··· 155 155 | Phishing (** "$phishing": The Email is likely to be phishing *) 156 156 | Junk (** "$junk": The Email is spam/junk *) 157 157 | NotJunk (** "$notjunk": The Email is explicitly marked as not spam/junk *) 158 + 159 + (* Apple Mail and other vendor extension keywords from draft-ietf-mailmaint-messageflag-mailboxattribute *) 160 + | Notify (** "$notify": Request to be notified when this email gets a reply *) 161 + | Muted (** "$muted": Email is muted (notifications disabled) *) 162 + | Followed (** "$followed": Email thread is followed for notifications *) 163 + | Memo (** "$memo": Email has a memo/note associated with it *) 164 + | HasMemo (** "$hasmemo": Email has a memo, annotation or note property *) 165 + | Autosent (** "$autosent": Email was generated or sent automatically *) 166 + | Unsubscribed (** "$unsubscribed": User has unsubscribed from this sender *) 167 + | CanUnsubscribe (** "$canunsubscribe": Email contains unsubscribe information *) 168 + | Imported (** "$imported": Email was imported from another system *) 169 + | IsTrusted (** "$istrusted": Email is from a trusted/verified sender *) 170 + | MaskedEmail (** "$maskedemail": Email is to/from a masked/anonymous address *) 171 + | New (** "$new": Email was recently delivered *) 172 + 173 + (* Apple Mail flag colors (color bit flags) *) 174 + | MailFlagBit0 (** "$MailFlagBit0": First color flag bit (red) *) 175 + | MailFlagBit1 (** "$MailFlagBit1": Second color flag bit (orange) *) 176 + | MailFlagBit2 (** "$MailFlagBit2": Third color flag bit (yellow) *) 158 177 | Custom of string (** Arbitrary user-defined keyword *) 159 178 160 179 (** A set of keywords applied to an email *) ··· 339 358 val take_id : t -> id 340 359 end 341 360 342 - (** Email import options. 343 - @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.5> RFC 8621, Section 4.5 *) 361 + (** Email/import method arguments and responses. 362 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.8> RFC 8621, Section 4.8 *) 363 + module Import : sig 364 + (** Arguments for Email/import method *) 365 + type args = { 366 + account_id : id; 367 + blob_ids : id list; 368 + mailbox_ids : id id_map; 369 + keywords : Keywords.t option; 370 + received_at : date option; 371 + } 372 + 373 + (** Create import arguments *) 374 + val create_args : 375 + account_id:id -> 376 + blob_ids:id list -> 377 + mailbox_ids:id id_map -> 378 + ?keywords:Keywords.t -> 379 + ?received_at:date -> 380 + unit -> args 381 + 382 + (** Response for a single imported email *) 383 + type email_import_result = { 384 + blob_id : id; 385 + email : Email.t; 386 + } 387 + 388 + (** Create an email import result *) 389 + val create_result : 390 + blob_id:id -> 391 + email:Email.t -> 392 + unit -> email_import_result 393 + 394 + (** Response for Email/import method *) 395 + type response = { 396 + account_id : id; 397 + created : email_import_result id_map; 398 + not_created : Jmap.Error.Set_error.t id_map; 399 + } 400 + 401 + (** Create import response *) 402 + val create_response : 403 + account_id:id -> 404 + created:email_import_result id_map -> 405 + not_created:Jmap.Error.Set_error.t id_map -> 406 + unit -> response 407 + end 408 + 409 + (** Email/parse method arguments and responses. 410 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.9> RFC 8621, Section 4.9 *) 411 + module Parse : sig 412 + (** Arguments for Email/parse method *) 413 + type args = { 414 + account_id : id; 415 + blob_ids : id list; 416 + properties : string list option; 417 + } 418 + 419 + (** Create parse arguments *) 420 + val create_args : 421 + account_id:id -> 422 + blob_ids:id list -> 423 + ?properties:string list -> 424 + unit -> args 425 + 426 + (** Response for a single parsed email *) 427 + type email_parse_result = { 428 + blob_id : id; 429 + parsed : Email.t; 430 + } 431 + 432 + (** Create an email parse result *) 433 + val create_result : 434 + blob_id:id -> 435 + parsed:Email.t -> 436 + unit -> email_parse_result 437 + 438 + (** Response for Email/parse method *) 439 + type response = { 440 + account_id : id; 441 + parsed : email_parse_result id_map; 442 + not_parsed : string id_map; 443 + } 444 + 445 + (** Create parse response *) 446 + val create_response : 447 + account_id:id -> 448 + parsed:email_parse_result id_map -> 449 + not_parsed:string id_map -> 450 + unit -> response 451 + end 452 + 453 + (** Email import options. 454 + @deprecated Use Import.args instead. 455 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.8> RFC 8621, Section 4.8 *) 344 456 type email_import_options = { 345 457 import_to_mailboxes : id list; 346 458 import_keywords : Keywords.t option; 347 459 import_received_at : date option; 348 460 } 349 461 462 + (** Email/copy method arguments and responses. 463 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.7> RFC 8621, Section 4.7 *) 464 + module Copy : sig 465 + (** Arguments for Email/copy method *) 466 + type args = { 467 + from_account_id : id; 468 + account_id : id; 469 + create : (id * id id_map) id_map; 470 + on_success_destroy_original : bool option; 471 + destroy_from_if_in_state : string option; 472 + } 473 + 474 + (** Create copy arguments *) 475 + val create_args : 476 + from_account_id:id -> 477 + account_id:id -> 478 + create:(id * id id_map) id_map -> 479 + ?on_success_destroy_original:bool -> 480 + ?destroy_from_if_in_state:string -> 481 + unit -> args 482 + 483 + (** Response for Email/copy method *) 484 + type response = { 485 + from_account_id : id; 486 + account_id : id; 487 + created : Email.t id_map option; 488 + not_created : Jmap.Error.Set_error.t id_map option; 489 + } 490 + 491 + (** Create copy response *) 492 + val create_response : 493 + from_account_id:id -> 494 + account_id:id -> 495 + ?created:Email.t id_map -> 496 + ?not_created:Jmap.Error.Set_error.t id_map -> 497 + unit -> response 498 + end 499 + 350 500 (** Email copy options. 351 - @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.6> RFC 8621, Section 4.6 *) 501 + @deprecated Use Copy.args instead. 502 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.7> RFC 8621, Section 4.7 *) 352 503 type email_copy_options = { 353 504 copy_to_account_id : id; 354 505 copy_to_mailboxes : id list;
+4
jmap-email/jmap_mailbox.mli
··· 14 14 | Trash (** Messages that have been deleted *) 15 15 | Junk (** Messages determined to be spam *) 16 16 | Important (** Messages deemed important *) 17 + | Snoozed (** Messages snoozed for later notification/reappearance, from draft-ietf-mailmaint-messageflag-mailboxattribute *) 18 + | Scheduled (** Messages scheduled for sending at a later time, from draft-ietf-mailmaint-messageflag-mailboxattribute *) 19 + | Memos (** Messages containing memos or notes, from draft-ietf-mailmaint-messageflag-mailboxattribute *) 20 + 17 21 | Other of string (** Custom or non-standard role *) 18 22 | None (** No specific role assigned *) 19 23
+84 -6
jmap-email/jmap_search_snippet.mli
··· 1 1 (** JMAP Search Snippet. 2 2 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5> RFC 8621, Section 5 *) 3 3 4 + open Jmap.Types 5 + open Jmap.Methods 6 + 4 7 (** SearchSnippet object. 5 - Note: Does not have an 'id' property. 8 + Provides highlighted snippets of emails matching search criteria. 9 + Note: Does not have an 'id' property; the key is the emailId. 6 10 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5> RFC 8621, Section 5 *) 7 - type t = { 8 - email_id : Jmap.Types.id; 9 - subject : string option; 10 - preview : string option; 11 - } 11 + module SearchSnippet : sig 12 + type t 13 + 14 + (** Get the email ID this snippet is for *) 15 + val email_id : t -> id 16 + 17 + (** Get the highlighted subject snippet (if matched) *) 18 + val subject : t -> string option 19 + 20 + (** Get the highlighted preview snippet (if matched) *) 21 + val preview : t -> string option 22 + 23 + (** Create a new SearchSnippet object *) 24 + val v : 25 + email_id:id -> 26 + ?subject:string -> 27 + ?preview:string -> 28 + unit -> t 29 + end 30 + 31 + (** {1 SearchSnippet Methods} *) 32 + 33 + (** Arguments for SearchSnippet/get. 34 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5.1> RFC 8621, Section 5.1 *) 35 + module Get_args : sig 36 + type t 37 + 38 + (** The account ID *) 39 + val account_id : t -> id 40 + 41 + (** The filter to use for the search *) 42 + val filter : t -> Filter.t 43 + 44 + (** Email IDs to return snippets for. If null, all matching emails are included *) 45 + val email_ids : t -> id list option 46 + 47 + (** Creation arguments *) 48 + val v : 49 + account_id:id -> 50 + filter:Filter.t -> 51 + ?email_ids:id list -> 52 + unit -> t 53 + end 54 + 55 + (** Response for SearchSnippet/get. 56 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5.1> RFC 8621, Section 5.1 *) 57 + module Get_response : sig 58 + type t 59 + 60 + (** The account ID *) 61 + val account_id : t -> id 62 + 63 + (** The search state string (for caching) *) 64 + val list : t -> SearchSnippet.t id_map 65 + 66 + (** IDs requested that weren't found *) 67 + val not_found : t -> id list 68 + 69 + (** Creation *) 70 + val v : 71 + account_id:id -> 72 + list:SearchSnippet.t id_map -> 73 + not_found:id list -> 74 + unit -> t 75 + end 76 + 77 + (** {1 Helper Functions} *) 78 + 79 + (** Helper to extract all matched keywords from a snippet. 80 + This parses highlighted portions from the snippet to get the actual search terms. *) 81 + val extract_matched_terms : string -> string list 82 + 83 + (** Helper to create a filter that searches in email body text. 84 + This is commonly used for SearchSnippet/get requests. *) 85 + val create_body_text_filter : string -> Filter.t 86 + 87 + (** Helper to create a filter that searches across multiple email fields. 88 + This searches subject, body, and headers for the given text. *) 89 + val create_fulltext_filter : string -> Filter.t
+116
jmap-email/jmap_thread.mli
··· 2 2 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3 *) 3 3 4 4 open Jmap.Types 5 + open Jmap.Methods 5 6 6 7 (** Thread object. 7 8 @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3 *) 8 9 module Thread : sig 9 10 type t 10 11 12 + (** Get the thread ID (server-set, immutable) *) 11 13 val id : t -> id 14 + 15 + (** Get the IDs of emails in the thread (server-set) *) 12 16 val email_ids : t -> id list 13 17 18 + (** Create a new Thread object *) 14 19 val v : id:id -> email_ids:id list -> t 15 20 end 21 + 22 + (** Thread properties that can be requested in Thread/get. 23 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.1> RFC 8621, Section 3.1 *) 24 + type property = 25 + | Id (** The Thread id *) 26 + | EmailIds (** The list of email IDs in the Thread *) 27 + 28 + (** Convert a property variant to its string representation *) 29 + val property_to_string : property -> string 30 + 31 + (** Parse a string into a property variant *) 32 + val string_to_property : string -> property 33 + 34 + (** Get a list of all standard Thread properties *) 35 + val all_properties : property list 36 + 37 + (** {1 Thread Methods} *) 38 + 39 + (** Arguments for Thread/get - extends standard get arguments. 40 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.1> RFC 8621, Section 3.1 *) 41 + module Get_args : sig 42 + type t 43 + 44 + val account_id : t -> id 45 + val ids : t -> id list option 46 + val properties : t -> string list option 47 + 48 + val v : 49 + account_id:id -> 50 + ?ids:id list -> 51 + ?properties:string list -> 52 + unit -> t 53 + end 54 + 55 + (** Response for Thread/get - extends standard get response. 56 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.1> RFC 8621, Section 3.1 *) 57 + module Get_response : sig 58 + type t 59 + 60 + val account_id : t -> id 61 + val state : t -> string 62 + val list : t -> Thread.t list 63 + val not_found : t -> id list 64 + 65 + val v : 66 + account_id:id -> 67 + state:string -> 68 + list:Thread.t list -> 69 + not_found:id list -> 70 + unit -> t 71 + end 72 + 73 + (** Arguments for Thread/changes - extends standard changes arguments. 74 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.2> RFC 8621, Section 3.2 *) 75 + module Changes_args : sig 76 + type t 77 + 78 + val account_id : t -> id 79 + val since_state : t -> string 80 + val max_changes : t -> uint option 81 + 82 + val v : 83 + account_id:id -> 84 + since_state:string -> 85 + ?max_changes:uint -> 86 + unit -> t 87 + end 88 + 89 + (** Response for Thread/changes - extends standard changes response. 90 + @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3.2> RFC 8621, Section 3.2 *) 91 + module Changes_response : sig 92 + type t 93 + 94 + val account_id : t -> id 95 + val old_state : t -> string 96 + val new_state : t -> string 97 + val has_more_changes : t -> bool 98 + val created : t -> id list 99 + val updated : t -> id list 100 + val destroyed : t -> id list 101 + 102 + val v : 103 + account_id:id -> 104 + old_state:string -> 105 + new_state:string -> 106 + has_more_changes:bool -> 107 + created:id list -> 108 + updated:id list -> 109 + destroyed:id list -> 110 + unit -> t 111 + end 112 + 113 + (** {1 Helper Functions} *) 114 + 115 + (** Create a filter to find threads with specific email ID *) 116 + val filter_has_email : id -> Filter.t 117 + 118 + (** Create a filter to find threads with emails from a specific sender *) 119 + val filter_from : string -> Filter.t 120 + 121 + (** Create a filter to find threads with emails to a specific recipient *) 122 + val filter_to : string -> Filter.t 123 + 124 + (** Create a filter to find threads with specific subject *) 125 + val filter_subject : string -> Filter.t 126 + 127 + (** Create a filter to find threads with emails received before a date *) 128 + val filter_before : date -> Filter.t 129 + 130 + (** Create a filter to find threads with emails received after a date *) 131 + val filter_after : date -> Filter.t