Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Jam is a tiny (~2kb gzipped), strongly-typed JMAP client with zero runtime dependencies. It has friendly, fluent APIs that make working with JMAP a breeze.

License

NotificationsYou must be signed in to change notification settings

htunnicliff/jmap-jam

Repository files navigation

Jam Illustration

Jam: A JMAP Client

bundle sizeJSRLicense

Jam is a tiny (~2kb gzipped), strongly-typed JMAP client with zero runtime dependencies. It has friendly, fluent APIs that make working with JMAP a breeze.

Jam is compatible with environments that support theWeb Fetch API andES Modules.

Jam adheres to the following IETF standards:

Table of Contents

Installation

Jam works in any environment that supports theWeb Fetch API andES Modules, including Node.js (>=18) and the browser.

Use as a package:

npm install jmap-jam

Use in the browser:

<scripttype="module">importJamClientfrom"https://your-preferred-cdn.com/jmap-jam@<version>";</script>

Getting Started

To initialize a client, provide the session URL for a JMAP server to connect to, as well as a bearer token for authenticating requests.

importJamClientfrom"jmap-jam";constjam=newJamClient({sessionUrl:"https://jmap.example.com/.well-known/jmap",bearerToken:"super-secret-token",});

Making Requests

JMAP is a meta protocol that makes performing multiple, dependent operations on a server more efficient by accepting batches of them in a single HTTP request.

A request is made up of one or moreinvocations (also known as method calls) that each specify a method, arguments, and a method call ID (an arbitrary string chosen by the requester). Method calls canreference each other with this ID, allowing for complex requests to be made.

To learn more about requests in JMAP, see the following resources:

Individual Requests

Here's what a single request looks like with Jam:

constjam=newJamClient({ ...});// Using convenience methodsconst[mailboxes]=awaitjam.api.Mailbox.get({accountId:"123"});// Using a plain requestconst[mailboxes]=awaitjam.request(["Mailbox/get",{accountId:"123"}]);

Both of these methods output the same JMAP request:

{"using": ["urn:ietf:params:jmap:mail"],"methodCalls": [    ["Mailbox/get",// <------------ Method name      {"accountId":"123" },// <--- Arguments"r1"// <------------- Method call ID (autogenerated)    ]  ]}

Convenience methods for available JMAP entities (e.g. Email, Mailbox, Thread) are available through theapi property.

Or, as seen in the example, requests can be made without convenience methods by using therequest method directly.

Both methods of sending requests have strongly typed responses and can be used interchangeably.

Multiple Requests

Though JMAP examples often show multiple method calls being used in a single request, see theNotes on Concurrency section for information about why a single method call per request can sometimes be preferred.

To send multiple method calls in a single request, userequestMany.

constjam=newJamClient({ ...});constaccountId='<account-id>';constmailboxId='<mailbox-id>';const[{ emails},meta]=awaitjam.requestMany((t)=>{// Get the first 10 email IDs in the mailboxconstemailIds=t.Email.query({    accountId,filter:{inMailbox:mailboxId,},limit:10,});// Get the emails with those IDsconstemails=t.Email.get({    accountId,ids:emailIds.$ref("/ids"),// Using a result referenceproperties:["id","htmlBody"],});return{ emailIds, emails};});

This produces the following JMAP request:

{"using": ["urn:ietf:params:jmap:mail"],"methodCalls": [    ["Email/query",      {"accountId":"<account-id>","filter": {"inMailbox":"<mailbox-id>"        }      },"emailIds"    ],    ["Email/get",      {"accountId":"<account-id>","#ids": {"name":"Email/query","resultOf":"emailIds","path":"/ids"        },"properties": ["id","htmlBody"]      },"emails"    ]  ]}

Thet argument used in therequestMany callback is aProxy that lets "invocation drafts" be definedbefore they are assembled into an actual JMAP request sent to the server.

To create aresult reference between invocations, use the$ref method on the invocation draft to be referenced.

Request Options

When making requests, you can pass an optionaloptions object as the second argument torequest,requestMany, or any of the convenience methods. This object accepts the following properties:

  • fetchInit - An object that will be passed to the Fetch APIfetch method as the second argument. This can be used to set headers, change the HTTP method, etc.
  • createdIds - A object containing client-specified creation IDs mapped to IDs the server assigned when each record was successfully created.
  • using - An array of additional JMAP capabilities to include when making the request.

Response Metadata

Convenience methods,request, andrequestMany all return a two-item tuple that contains the response data and metadata.

const[mailboxes,meta]=awaitjam.api.Mailbox.get({accountId:"123"});const{ sessionState, createdIds, response}=meta;

The meta object contains the following properties:

  • sessionState - The current session state.
  • createdIds - A map of method call IDs to the IDs of any objects created by the server in response to the request.
  • response - The actual Fetch APIResponse.

Notes on Concurrency

RFC 8620 § 3.10: Method calls within a single request MUST be executed in order [by the server]. However, method calls from different concurrent API requests may be interleaved. This means that the data on the server may change between two method calls within a single API request.

JMAP supports passing multiple method calls in a single request, but it is important to remember that each method call will be executed in sequence, not concurrently.

TypeScript

Jam provides types for JMAP methods, arguments, and responses as described in theJMAP andJMAP Mail RFCs.

Allconvenience methods,request, andrequestMany will reveal autosuggested types for method names (e.g.Email/get), the arguments for that method, and the appropriate response.

Many response types will infer from arguments. For example, when using an argument field such asproperties to filter fields in a response, the response type will be narrowed to exclude fields that were not included.

Capabilities

Jam has strongly-typed support for the following JMAP capabilities:

EntityCapability Identifier
Coreurn:ietf:params:jmap:core
Mailboxurn:ietf:params:jmap:mail
Threadurn:ietf:params:jmap:mail
Emailurn:ietf:params:jmap:mail
SearchSnippeturn:ietf:params:jmap:mail
Identityurn:ietf:params:jmap:submission
EmailSubmissionurn:ietf:params:jmap:submission
VacationResponseurn:ietf:params:jmap:vacationresponse

API Reference

JamClient

JamClient is Jam's primary entrypoint. To use Jam, import and construct an instance.

The class can be imported by name or using default import syntax.

importJamClientfrom"jmap-jam";constjam=newJamClient({bearerToken:"<bearer-token>",sessionUrl:"<server-session-url>",});

A client instance requires both abearerToken andsessionUrl in order to make authenticated requests.

Upon constructing a client, Jam will immediately dispatch a request for asession from the server. This session will be used for all subsequent requests.

api.<entity>.<operation>()

A convenience pattern for making individual JMAP requests that uses therequest method under the hood.

const[mailboxes]=awaitjam.api.Mailbox.get({  accountId,});const[emails]=awaitjam.api.Email.get({  accountId,ids:["email-123"],properties:["subject"],});

request()

Send a standard JMAP request.

const[mailboxes]=awaitjam.request(["Mailbox/get",{ accountId}]);const[emails]=awaitjam.request(["Email/get",{    accountId,ids:["email-123"],properties:["subject"],},]);

requestMany()

Send a JMAP request with multiple method calls.

const[{ emailIds, emails}]=awaitjam.requestMany((r)=>{constemailIds=r.Email.query({    accountId,filter:{inMailbox:mailboxId,},});constemails=r.Email.get({    accountId,ids:emailIds.$ref("/ids"),properties:["id","htmlBody"],});return{ emailIds, emails};});

$ref()

Each item created within arequestMany callback is an instance ofInvocationDraft. Internally, it keeps track of the invocation that was defined for use when the request is finalized and sent.

The important part ofInvocationDraft is that each draft exposes a method$ref that can be used to create aresult reference between invocations.

To create a result reference, call$ref with a JSON pointer at the field that will receive the reference.

TheemailIds.$ref("/ids") call in the previous code block will be transformed into this valid JMAP result reference before the request is sent:

{"using": ["urn:ietf:params:jmap:mail"],"methodCalls": [    ["Email/query",      {"accountId":"<account-id>","filter": {"inMailbox":"<mailbox-id>"        }      },"emailIds"    ],    ["Email/get",      {"accountId":"<account-id>",// Result reference created here"#ids": {"name":"Email/query","resultOf":"emailIds","path":"/ids"        },"properties": ["id","htmlBody"]      },"emails"    ]  ]}

session

Get the client's current session.

constsession=awaitjam.session;

getPrimaryAccount()

Get the ID of the primary mail account for the current session.

constaccountId=awaitjam.getPrimaryAccount();

uploadBlob()

Initiate a fetch request to upload a blob.

constdata=awaitjam.uploadBlob(accountId,newBlob(["hello world"],{type:"text/plain"}));console.log(data);// =>// {//   accountId: "account-abcd",//   blobId: "blob-123",//   type: "text/plain",//   size: 152,// }

downloadBlob()

Intiate a fetch request to download a specific blob. Downloading a blob requires both aMIME type and file name, since JMAP server implementations are not required to store this information.

If the JMAP server sets aContent-Type header in its response, it will use the value provided inmimeType.

If the JMAP server sets aContent-Disposition header in its response, it will use the value provided infileName.

constresponse=awaitjam.downloadBlob({  accountId,blobId:'blob-123'mimeType:'image/png'fileName:'photo.png'});constblob=awaitresponse.blob();// or response.arrayBuffer()// or response.text()// ...etc

connectEventSource()

Connect to a JMAP event source usingServer-Sent Events.

constsse=awaitjam.connectEventSource({types:"*",// or ["Mailbox", "Email", ...]ping:5000,// ping interval in millisecondscloseafter:"no",// or "state"});sse.addEventListener("message",(event)=> ...));sse.addEventListener("error",(event)=> ...));sse.addEventListener("close",(event)=> ...));

About

Jam is a tiny (~2kb gzipped), strongly-typed JMAP client with zero runtime dependencies. It has friendly, fluent APIs that make working with JMAP a breeze.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project


    [8]ページ先頭

    ©2009-2025 Movatter.jp