this repo has no description

Add core JMAP type definitions based on RFC8620

Implements comprehensive OCaml type definitions for the JMAP protocol:
- Basic data types (id, int_t, unsigned_int, date, utc_date)
- Core objects (error, set_error, invocation, result_reference)
- Filter and sorting mechanisms
- Account and session management
- Push notification structures
- Request/response structures
- Standard method argument and response types

All types are documented with corresponding RFC section references.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+618 -1
lib
+4 -1
AGENT.md
···
Read your instructions from this file, and mark successfully completed instructions
with DONE so that you will know what to do next when reinvoked in the future.
-
1. Define core OCaml type definitions corresponding to the JMAP protocol
+
1. DONE Define core OCaml type definitions corresponding to the JMAP protocol
specification, in a new Jmap.Types module.
+
2. Add a `Jmap_api` module to make JMAP API requests over HTTP and parse the
+
responses into the `Jmap_types`. Use `Cohttp_lwt_unix` for the HTTP library.
+
3. Add an implementation of the Jmap_session handling.
+307
lib/jmap.ml
···
+
(**
+
* JMAP protocol implementation based on RFC8620
+
* https://datatracker.ietf.org/doc/html/rfc8620
+
*)
+
+
module Types = struct
+
(** Id string as per Section 1.2 *)
+
type id = string
+
+
(** Int bounded within the range -2^53+1 to 2^53-1 as per Section 1.3 *)
+
type int_t = int
+
+
(** UnsignedInt bounded within the range 0 to 2^53-1 as per Section 1.3 *)
+
type unsigned_int = int
+
+
(** Date string in RFC3339 format as per Section 1.4 *)
+
type date = string
+
+
(** UTCDate is a Date with 'Z' time zone as per Section 1.4 *)
+
type utc_date = string
+
+
(** Error object as per Section 3.6.2 *)
+
type error = {
+
type_: string;
+
description: string option;
+
}
+
+
(** Set error object as per Section 5.3 *)
+
type set_error = {
+
type_: string;
+
description: string option;
+
properties: string list option;
+
(* Additional properties for specific error types *)
+
existing_id: id option; (* For alreadyExists error *)
+
}
+
+
(** Invocation object as per Section 3.2 *)
+
type 'a invocation = {
+
name: string;
+
arguments: 'a;
+
method_call_id: string;
+
}
+
+
(** ResultReference object as per Section 3.7 *)
+
type result_reference = {
+
result_of: string;
+
name: string;
+
path: string;
+
}
+
+
(** FilterOperator, FilterCondition and Filter as per Section 5.5 *)
+
type filter_operator = {
+
operator: string; (* "AND", "OR", "NOT" *)
+
conditions: filter list;
+
}
+
and filter_condition = (string * Ezjsonm.value) list
+
and filter =
+
| Operator of filter_operator
+
| Condition of filter_condition
+
+
(** Comparator object for sorting as per Section 5.5 *)
+
type comparator = {
+
property: string;
+
is_ascending: bool option; (* Optional, defaults to true *)
+
collation: string option; (* Optional, server-dependent default *)
+
}
+
+
(** PatchObject as per Section 5.3 *)
+
type patch_object = (string * Ezjsonm.value) list
+
+
(** AddedItem structure as per Section 5.6 *)
+
type added_item = {
+
id: id;
+
index: unsigned_int;
+
}
+
+
(** Account object as per Section 1.6.2 *)
+
type account = {
+
name: string;
+
is_personal: bool;
+
is_read_only: bool;
+
account_capabilities: (string * Ezjsonm.value) list;
+
}
+
+
(** Core capability object as per Section 2 *)
+
type core_capability = {
+
max_size_upload: unsigned_int;
+
max_concurrent_upload: unsigned_int;
+
max_size_request: unsigned_int;
+
max_concurrent_requests: unsigned_int;
+
max_calls_in_request: unsigned_int;
+
max_objects_in_get: unsigned_int;
+
max_objects_in_set: unsigned_int;
+
collation_algorithms: string list;
+
}
+
+
(** PushSubscription keys object as per Section 7.2 *)
+
type push_keys = {
+
p256dh: string;
+
auth: string;
+
}
+
+
(** Session object as per Section 2 *)
+
type session = {
+
capabilities: (string * Ezjsonm.value) list;
+
accounts: (id * account) list;
+
primary_accounts: (string * id) list;
+
username: string;
+
api_url: string;
+
download_url: string;
+
upload_url: string;
+
event_source_url: string option;
+
state: string;
+
}
+
+
(** TypeState for state changes as per Section 7.1 *)
+
type type_state = (string * string) list
+
+
(** StateChange object as per Section 7.1 *)
+
type state_change = {
+
changed: (id * type_state) list;
+
}
+
+
(** PushVerification object as per Section 7.2.2 *)
+
type push_verification = {
+
push_subscription_id: id;
+
verification_code: string;
+
}
+
+
(** PushSubscription object as per Section 7.2 *)
+
type push_subscription = {
+
id: id;
+
device_client_id: string;
+
url: string;
+
keys: push_keys option;
+
verification_code: string option;
+
expires: utc_date option;
+
types: string list option;
+
}
+
+
(** Request object as per Section 3.3 *)
+
type request = {
+
using: string list;
+
method_calls: Ezjsonm.value invocation list;
+
created_ids: (id * id) list option;
+
}
+
+
(** Response object as per Section 3.4 *)
+
type response = {
+
method_responses: Ezjsonm.value invocation list;
+
created_ids: (id * id) list option;
+
session_state: string;
+
}
+
+
(** Standard method arguments and responses *)
+
+
(** Arguments for Foo/get method as per Section 5.1 *)
+
type 'a get_arguments = {
+
account_id: id;
+
ids: id list option;
+
properties: string list option;
+
}
+
+
(** Response for Foo/get method as per Section 5.1 *)
+
type 'a get_response = {
+
account_id: id;
+
state: string;
+
list: 'a list;
+
not_found: id list;
+
}
+
+
(** Arguments for Foo/changes method as per Section 5.2 *)
+
type changes_arguments = {
+
account_id: id;
+
since_state: string;
+
max_changes: unsigned_int option;
+
}
+
+
(** Response for Foo/changes method as per Section 5.2 *)
+
type changes_response = {
+
account_id: id;
+
old_state: string;
+
new_state: string;
+
has_more_changes: bool;
+
created: id list;
+
updated: id list;
+
destroyed: id list;
+
}
+
+
(** Arguments for Foo/set method as per Section 5.3 *)
+
type 'a set_arguments = {
+
account_id: id;
+
if_in_state: string option;
+
create: (id * 'a) list option;
+
update: (id * patch_object) list option;
+
destroy: id list option;
+
}
+
+
(** Response for Foo/set method as per Section 5.3 *)
+
type 'a set_response = {
+
account_id: id;
+
old_state: string option;
+
new_state: string;
+
created: (id * 'a) list option;
+
updated: (id * 'a option) list option;
+
destroyed: id list option;
+
not_created: (id * set_error) list option;
+
not_updated: (id * set_error) list option;
+
not_destroyed: (id * set_error) list option;
+
}
+
+
(** Arguments for Foo/copy method as per Section 5.4 *)
+
type 'a copy_arguments = {
+
from_account_id: id;
+
if_from_in_state: string option;
+
account_id: id;
+
if_in_state: string option;
+
create: (id * 'a) list;
+
on_success_destroy_original: bool option;
+
destroy_from_if_in_state: string option;
+
}
+
+
(** Response for Foo/copy method as per Section 5.4 *)
+
type 'a copy_response = {
+
from_account_id: id;
+
account_id: id;
+
old_state: string option;
+
new_state: string;
+
created: (id * 'a) list option;
+
not_created: (id * set_error) list option;
+
}
+
+
(** Arguments for Foo/query method as per Section 5.5 *)
+
type query_arguments = {
+
account_id: id;
+
filter: filter option;
+
sort: comparator list option;
+
position: int_t option;
+
anchor: id option;
+
anchor_offset: int_t option;
+
limit: unsigned_int option;
+
calculate_total: bool option;
+
}
+
+
(** Response for Foo/query method as per Section 5.5 *)
+
type query_response = {
+
account_id: id;
+
query_state: string;
+
can_calculate_changes: bool;
+
position: unsigned_int;
+
ids: id list;
+
total: unsigned_int option;
+
limit: unsigned_int option;
+
}
+
+
(** Arguments for Foo/queryChanges method as per Section 5.6 *)
+
type query_changes_arguments = {
+
account_id: id;
+
filter: filter option;
+
sort: comparator list option;
+
since_query_state: string;
+
max_changes: unsigned_int option;
+
up_to_id: id option;
+
calculate_total: bool option;
+
}
+
+
(** Response for Foo/queryChanges method as per Section 5.6 *)
+
type query_changes_response = {
+
account_id: id;
+
old_query_state: string;
+
new_query_state: string;
+
total: unsigned_int option;
+
removed: id list;
+
added: added_item list option;
+
}
+
+
(** Arguments for Blob/copy method as per Section 6.3 *)
+
type blob_copy_arguments = {
+
from_account_id: id;
+
account_id: id;
+
blob_ids: id list;
+
}
+
+
(** Response for Blob/copy method as per Section 6.3 *)
+
type blob_copy_response = {
+
from_account_id: id;
+
account_id: id;
+
copied: (id * id) list option;
+
not_copied: (id * set_error) list option;
+
}
+
+
(** Upload response as per Section 6.1 *)
+
type upload_response = {
+
account_id: id;
+
blob_id: id;
+
type_: string;
+
size: unsigned_int;
+
}
+
+
(** Problem details object as per RFC7807 and Section 3.6.1 *)
+
type problem_details = {
+
type_: string;
+
status: int option;
+
detail: string option;
+
limit: string option; (* For "limit" error *)
+
}
+
end
+307
lib/jmap.mli
···
+
(**
+
* JMAP protocol implementation based on RFC8620
+
* https://datatracker.ietf.org/doc/html/rfc8620
+
*)
+
+
module Types : sig
+
(** Id string as per Section 1.2 *)
+
type id = string
+
+
(** Int bounded within the range -2^53+1 to 2^53-1 as per Section 1.3 *)
+
type int_t = int
+
+
(** UnsignedInt bounded within the range 0 to 2^53-1 as per Section 1.3 *)
+
type unsigned_int = int
+
+
(** Date string in RFC3339 format as per Section 1.4 *)
+
type date = string
+
+
(** UTCDate is a Date with 'Z' time zone as per Section 1.4 *)
+
type utc_date = string
+
+
(** Error object as per Section 3.6.2 *)
+
type error = {
+
type_: string;
+
description: string option;
+
}
+
+
(** Set error object as per Section 5.3 *)
+
type set_error = {
+
type_: string;
+
description: string option;
+
properties: string list option;
+
(* Additional properties for specific error types *)
+
existing_id: id option; (* For alreadyExists error *)
+
}
+
+
(** Invocation object as per Section 3.2 *)
+
type 'a invocation = {
+
name: string;
+
arguments: 'a;
+
method_call_id: string;
+
}
+
+
(** ResultReference object as per Section 3.7 *)
+
type result_reference = {
+
result_of: string;
+
name: string;
+
path: string;
+
}
+
+
(** FilterOperator, FilterCondition and Filter as per Section 5.5 *)
+
type filter_operator = {
+
operator: string; (* "AND", "OR", "NOT" *)
+
conditions: filter list;
+
}
+
and filter_condition = (string * Ezjsonm.value) list
+
and filter =
+
| Operator of filter_operator
+
| Condition of filter_condition
+
+
(** Comparator object for sorting as per Section 5.5 *)
+
type comparator = {
+
property: string;
+
is_ascending: bool option; (* Optional, defaults to true *)
+
collation: string option; (* Optional, server-dependent default *)
+
}
+
+
(** PatchObject as per Section 5.3 *)
+
type patch_object = (string * Ezjsonm.value) list
+
+
(** AddedItem structure as per Section 5.6 *)
+
type added_item = {
+
id: id;
+
index: unsigned_int;
+
}
+
+
(** Account object as per Section 1.6.2 *)
+
type account = {
+
name: string;
+
is_personal: bool;
+
is_read_only: bool;
+
account_capabilities: (string * Ezjsonm.value) list;
+
}
+
+
(** Core capability object as per Section 2 *)
+
type core_capability = {
+
max_size_upload: unsigned_int;
+
max_concurrent_upload: unsigned_int;
+
max_size_request: unsigned_int;
+
max_concurrent_requests: unsigned_int;
+
max_calls_in_request: unsigned_int;
+
max_objects_in_get: unsigned_int;
+
max_objects_in_set: unsigned_int;
+
collation_algorithms: string list;
+
}
+
+
(** PushSubscription keys object as per Section 7.2 *)
+
type push_keys = {
+
p256dh: string;
+
auth: string;
+
}
+
+
(** Session object as per Section 2 *)
+
type session = {
+
capabilities: (string * Ezjsonm.value) list;
+
accounts: (id * account) list;
+
primary_accounts: (string * id) list;
+
username: string;
+
api_url: string;
+
download_url: string;
+
upload_url: string;
+
event_source_url: string option;
+
state: string;
+
}
+
+
(** TypeState for state changes as per Section 7.1 *)
+
type type_state = (string * string) list
+
+
(** StateChange object as per Section 7.1 *)
+
type state_change = {
+
changed: (id * type_state) list;
+
}
+
+
(** PushVerification object as per Section 7.2.2 *)
+
type push_verification = {
+
push_subscription_id: id;
+
verification_code: string;
+
}
+
+
(** PushSubscription object as per Section 7.2 *)
+
type push_subscription = {
+
id: id;
+
device_client_id: string;
+
url: string;
+
keys: push_keys option;
+
verification_code: string option;
+
expires: utc_date option;
+
types: string list option;
+
}
+
+
(** Request object as per Section 3.3 *)
+
type request = {
+
using: string list;
+
method_calls: Ezjsonm.value invocation list;
+
created_ids: (id * id) list option;
+
}
+
+
(** Response object as per Section 3.4 *)
+
type response = {
+
method_responses: Ezjsonm.value invocation list;
+
created_ids: (id * id) list option;
+
session_state: string;
+
}
+
+
(** Standard method arguments and responses *)
+
+
(** Arguments for Foo/get method as per Section 5.1 *)
+
type 'a get_arguments = {
+
account_id: id;
+
ids: id list option;
+
properties: string list option;
+
}
+
+
(** Response for Foo/get method as per Section 5.1 *)
+
type 'a get_response = {
+
account_id: id;
+
state: string;
+
list: 'a list;
+
not_found: id list;
+
}
+
+
(** Arguments for Foo/changes method as per Section 5.2 *)
+
type changes_arguments = {
+
account_id: id;
+
since_state: string;
+
max_changes: unsigned_int option;
+
}
+
+
(** Response for Foo/changes method as per Section 5.2 *)
+
type changes_response = {
+
account_id: id;
+
old_state: string;
+
new_state: string;
+
has_more_changes: bool;
+
created: id list;
+
updated: id list;
+
destroyed: id list;
+
}
+
+
(** Arguments for Foo/set method as per Section 5.3 *)
+
type 'a set_arguments = {
+
account_id: id;
+
if_in_state: string option;
+
create: (id * 'a) list option;
+
update: (id * patch_object) list option;
+
destroy: id list option;
+
}
+
+
(** Response for Foo/set method as per Section 5.3 *)
+
type 'a set_response = {
+
account_id: id;
+
old_state: string option;
+
new_state: string;
+
created: (id * 'a) list option;
+
updated: (id * 'a option) list option;
+
destroyed: id list option;
+
not_created: (id * set_error) list option;
+
not_updated: (id * set_error) list option;
+
not_destroyed: (id * set_error) list option;
+
}
+
+
(** Arguments for Foo/copy method as per Section 5.4 *)
+
type 'a copy_arguments = {
+
from_account_id: id;
+
if_from_in_state: string option;
+
account_id: id;
+
if_in_state: string option;
+
create: (id * 'a) list;
+
on_success_destroy_original: bool option;
+
destroy_from_if_in_state: string option;
+
}
+
+
(** Response for Foo/copy method as per Section 5.4 *)
+
type 'a copy_response = {
+
from_account_id: id;
+
account_id: id;
+
old_state: string option;
+
new_state: string;
+
created: (id * 'a) list option;
+
not_created: (id * set_error) list option;
+
}
+
+
(** Arguments for Foo/query method as per Section 5.5 *)
+
type query_arguments = {
+
account_id: id;
+
filter: filter option;
+
sort: comparator list option;
+
position: int_t option;
+
anchor: id option;
+
anchor_offset: int_t option;
+
limit: unsigned_int option;
+
calculate_total: bool option;
+
}
+
+
(** Response for Foo/query method as per Section 5.5 *)
+
type query_response = {
+
account_id: id;
+
query_state: string;
+
can_calculate_changes: bool;
+
position: unsigned_int;
+
ids: id list;
+
total: unsigned_int option;
+
limit: unsigned_int option;
+
}
+
+
(** Arguments for Foo/queryChanges method as per Section 5.6 *)
+
type query_changes_arguments = {
+
account_id: id;
+
filter: filter option;
+
sort: comparator list option;
+
since_query_state: string;
+
max_changes: unsigned_int option;
+
up_to_id: id option;
+
calculate_total: bool option;
+
}
+
+
(** Response for Foo/queryChanges method as per Section 5.6 *)
+
type query_changes_response = {
+
account_id: id;
+
old_query_state: string;
+
new_query_state: string;
+
total: unsigned_int option;
+
removed: id list;
+
added: added_item list option;
+
}
+
+
(** Arguments for Blob/copy method as per Section 6.3 *)
+
type blob_copy_arguments = {
+
from_account_id: id;
+
account_id: id;
+
blob_ids: id list;
+
}
+
+
(** Response for Blob/copy method as per Section 6.3 *)
+
type blob_copy_response = {
+
from_account_id: id;
+
account_id: id;
+
copied: (id * id) list option;
+
not_copied: (id * set_error) list option;
+
}
+
+
(** Upload response as per Section 6.1 *)
+
type upload_response = {
+
account_id: id;
+
blob_id: id;
+
type_: string;
+
size: unsigned_int;
+
}
+
+
(** Problem details object as per RFC7807 and Section 3.6.1 *)
+
type problem_details = {
+
type_: string;
+
status: int option;
+
detail: string option;
+
limit: string option; (* For "limit" error *)
+
}
+
end