|
3 | 3 | <h1 align="center">Jam: A JMAP Client</h1>
|
4 | 4 | </div>
|
5 | 5 |
|
6 |
| -A tiny (<2kb gzipped), typed JMAP client with zero runtime dependencies, adhering to the following IETF standards: |
| 6 | +Jame is a tiny (<3kb gzipped), strongly-typed JMAP client with zero runtime dependencies. It has friendly, fluent APIs that make working with JMAP a breeze. |
7 | 7 |
|
8 |
| -- [RFC 8620][jmap-rfc] - JMAP |
9 |
| -- [RFC 8621][jmap-mail-rfc] - JMAP for Mail |
| 8 | +Jam is compatible with environments that support the [Web Fetch API][mdn-using-fetch] and [ES Modules][mdn-esm]. |
10 | 9 |
|
11 |
| -> [!IMPORTANT] |
12 |
| -> Version `0.x` is considered unstable. Breaking changes may occur until version `1.0` is published. |
| 10 | +Jam adheres to the following IETF standards: |
13 | 11 |
|
14 |
| -### To-do |
15 |
| - |
16 |
| -- [ ] [RFC 8887][jmap-ws-rfc] - JMAP Subprotocol for WebSocket |
| 12 | +- [RFC 8620][jmap-rfc] - JMAP |
| 13 | +- [RFC 8621][jmap-mail-rfc] - JMAP for Mail |
17 | 14 |
|
18 | 15 | [jmap-rfc]: https://datatracker.ietf.org/doc/html/rfc8620
|
19 | 16 | [jmap-mail-rfc]: https://datatracker.ietf.org/doc/html/rfc8621
|
20 |
| -[jmap-ws-rfc]: https://datatracker.ietf.org/doc/html/rfc8887 |
| 17 | +[mdn-esm]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules |
21 | 18 |
|
22 | 19 | ### Table of Contents
|
23 | 20 |
|
24 | 21 | - [Installation](#installation)
|
25 | 22 | - [Getting Started](#getting-started)
|
26 | 23 | - [Making Requests](#making-requests)
|
| 24 | + - [Individual Requests](#individual-requests) |
| 25 | + - [Multiple Requests](#multiple-requests) |
| 26 | + - [Request Options](#request-options) |
| 27 | + - [Response Metadata](#response-metadata) |
27 | 28 | - [Notes on Concurrency](#notes-on-concurrency)
|
28 | 29 | - [TypeScript](#typescript)
|
| 30 | +- [Capabilities](#capabilities) |
29 | 31 | - [API Reference](#api-reference)
|
30 |
| - - [`#session`](#session) |
| 32 | + - [`JamClient`](#jamclient) |
31 | 33 | - [`#api.<entity>.<operation>()`](#apientityoperation)
|
32 | 34 | - [`request()`](#request)
|
| 35 | + - [`requestMany()`](#requestmany) |
| 36 | + - [`$ref()`](#ref) |
| 37 | + - [`#session`](#session) |
33 | 38 | - [`getPrimaryAccount()`](#getprimaryaccount)
|
34 | 39 | - [`downloadBlob()`](#downloadblob)
|
35 | 40 | - [`uploadBlob()`](#uploadblob)
|
@@ -122,7 +127,7 @@ Both methods of sending requests have strongly typed responses and can be used i
|
122 | 127 |
|
123 | 128 | > Though JMAP examples often show multiple method calls being used in a single request, see the [Notes on Concurrency](#notes-on-concurrency) section for information about why a single method call per request can sometimes be preferred.
|
124 | 129 |
|
125 |
| -To send multiple method calls in a single request, use `requestMany`. |
| 130 | +To send multiple method calls in a single request, use [`requestMany`](#requestmany). |
126 | 131 |
|
127 | 132 | ```ts
|
128 | 133 | const jam = new JamClient({ ... });
|
@@ -227,12 +232,48 @@ JMAP supports passing multiple method calls in a single request, but it is impor
|
227 | 232 |
|
228 | 233 | Jam provides types for JMAP methods, arguments, and responses as described in the [JMAP][jmap-rfc] and [JMAP Mail][jmap-mail-rfc] RFCs.
|
229 | 234 |
|
230 |
| -All convenience methods, `request`, and `requestMany` will reveal autosuggested types for method names (e.g. `Email/get`), the arguments for that method, and the appropriate response. |
| 235 | +All [convenience methods](#apientityoperation), [`request`](#request), and [`requestMany`](#requestmany) will reveal autosuggested types for method names (e.g. `Email/get`), the arguments for that method, and the appropriate response. |
231 | 236 |
|
232 | 237 | Many response types will infer from arguments. For example, when using an argument field such as `properties` to filter fields in a response, the response type will be narrowed to exclude fields that were not included.
|
233 | 238 |
|
| 239 | +## Capabilities |
| 240 | + |
| 241 | +Jam has strongly-typed support for the following JMAP capabilities: |
| 242 | + |
| 243 | +| Entity | Capability Identifier | |
| 244 | +| ---------------- | --------------------------------------- | |
| 245 | +| Core | `urn:ietf:params:jmap:core` | |
| 246 | +| Mailbox | `urn:ietf:params:jmap:mail` | |
| 247 | +| Thread | `urn:ietf:params:jmap:mail` | |
| 248 | +| Email | `urn:ietf:params:jmap:mail` | |
| 249 | +| SearchSnippet | `urn:ietf:params:jmap:mail` | |
| 250 | +| Identity | `urn:ietf:params:jmap:submission` | |
| 251 | +| EmailSubmission | `urn:ietf:params:jmap:submission` | |
| 252 | +| VacationResponse | `urn:ietf:params:jmap:vacationresponse` | |
| 253 | + |
234 | 254 | ## API Reference
|
235 | 255 |
|
| 256 | +### `JamClient` |
| 257 | + |
| 258 | +`JamClient` is Jam's primary entrypoint. To use Jam, import and construct an instance. |
| 259 | + |
| 260 | +The class can be imported by name or using default import syntax. |
| 261 | + |
| 262 | +```ts |
| 263 | +import JamClient from "jmap-jam"; |
| 264 | + |
| 265 | +const jam = new JamClient({ |
| 266 | + bearerToken: "<bearer-token>", |
| 267 | + sessionUrl: "<server-session-url>", |
| 268 | +}); |
| 269 | +``` |
| 270 | + |
| 271 | +A client instance requires both a `bearerToken` and `sessionUrl` in order to make authenticated requests. |
| 272 | + |
| 273 | +Upon constructing a client, Jam will immediately dispatch a request for a [session][jmap-section-2] from the server. This session will be used for all subsequent requests. |
| 274 | + |
| 275 | +[jmap-section-2]: https://datatracker.ietf.org/doc/html/rfc8620#section-2 |
| 276 | + |
236 | 277 | ### `#api.<entity>.<operation>()`
|
237 | 278 |
|
238 | 279 | A convenience pattern for making individual JMAP requests that uses the [`request`](#request) method under the hood.
|
@@ -289,6 +330,48 @@ const [{ emailIds, emails }] = await jam.requestMany((r) => {
|
289 | 330 | });
|
290 | 331 | ```
|
291 | 332 |
|
| 333 | +#### `$ref()` |
| 334 | + |
| 335 | +Each item created within a `requestMany` callback is an instance of `InvocationDraft`. Internally, it keeps track of the invocation that was defined for use when the request is finalized and sent. |
| 336 | + |
| 337 | +The important part of `InvocationDraft` is that each draft exposes a method `$ref` that can be used to create a [result reference][jmap-3.7-result-refs] between invocations. |
| 338 | + |
| 339 | +To create a result reference, call `$ref` with a JSON pointer at the field that will receive the reference. |
| 340 | + |
| 341 | +The `emailIds.$ref("/ids")` call in the previous code block will be transformed into this valid JMAP result reference before the request is sent: |
| 342 | + |
| 343 | +```jsonc |
| 344 | +{ |
| 345 | + "using": ["urn:ietf:params:jmap:mail"], |
| 346 | + "methodCalls": [ |
| 347 | + [ |
| 348 | + "Email/query", |
| 349 | + { |
| 350 | + "accountId": "<account-id>", |
| 351 | + "filter": { |
| 352 | + "inMailbox": "<mailbox-id>" |
| 353 | + } |
| 354 | + }, |
| 355 | + "emailIds" |
| 356 | + ], |
| 357 | + [ |
| 358 | + "Email/get", |
| 359 | + { |
| 360 | + "accountId": "<account-id>", |
| 361 | + // Result reference created here |
| 362 | + "#ids": { |
| 363 | + "name": "Email/query", |
| 364 | + "resultOf": "emailIds", |
| 365 | + "path": "/ids" |
| 366 | + }, |
| 367 | + "properties": ["id", "htmlBody"] |
| 368 | + }, |
| 369 | + "emails" |
| 370 | + ] |
| 371 | + ] |
| 372 | +} |
| 373 | +``` |
| 374 | + |
292 | 375 | ### `#session`
|
293 | 376 |
|
294 | 377 | Get the client's current session.
|
@@ -349,9 +432,6 @@ console.log(data); // =>
|
349 | 432 |
|
350 | 433 | Connect to a JMAP event source using [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
|
351 | 434 |
|
352 |
| -> [!NOTE] |
353 |
| -> At the time of this writing, the popular JMAP server host Fastmail has not implemented support for server-sent events. |
354 |
| -
|
355 | 435 | ```js
|
356 | 436 | const sse = await client.connectEventSource({
|
357 | 437 | types: "*", // or ["Mailbox", "Email", ...]
|
|
0 commit comments