mailpit-api 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mjs/index.js CHANGED
@@ -1,6 +1,33 @@
1
1
  import axios, { isAxiosError, } from "axios";
2
+ /**
3
+ * Client for interacting with the {@link https://mailpit.axllent.org/docs/api-v1/ | Mailpit API}.
4
+ * @example
5
+ * ```typescript
6
+ * import { MailpitClient } from "mailpit-api";
7
+ * const mailpit = new MailpitClient("http://localhost:8025");
8
+ * console.log(await mailpit.getInfo());
9
+ * ```
10
+ */
2
11
  export class MailpitClient {
3
12
  axiosInstance;
13
+ /**
14
+ * Creates an instance of {@link MailpitClient}.
15
+ * @param baseURL - The base URL of the Mailpit API.
16
+ * @param auth - Optional authentication credentials.
17
+ * @param auth.username - The username for basic authentication.
18
+ * @param auth.password - The password for basic authentication.
19
+ * @example No Auth
20
+ * ```typescript
21
+ * const mailpit = new MailpitClient("http://localhost:8025");
22
+ * ```
23
+ * @example Basic Auth
24
+ * ```typescript
25
+ * const mailpit = new MailpitClient("http://localhost:8025", {
26
+ * username: "admin",
27
+ * password: "supersecret",
28
+ * });
29
+ * ```
30
+ */
4
31
  constructor(baseURL, auth) {
5
32
  this.axiosInstance = axios.create({
6
33
  baseURL,
@@ -37,20 +64,69 @@ export class MailpitClient {
37
64
  }
38
65
  }
39
66
  }
40
- // Application
67
+ /**
68
+ * Retrieves information about the Mailpit instance.
69
+ *
70
+ * @returns Basic runtime information, message totals and latest release version.
71
+ * @example
72
+ * ```typescript
73
+ * const info = await mailpit.getInfo();
74
+ * ```
75
+ */
41
76
  async getInfo() {
42
77
  return await this.handleRequest(() => this.axiosInstance.get("/api/v1/info"));
43
78
  }
79
+ /**
80
+ * Retrieves the configuration of the Mailpit web UI.
81
+ * @remarks Intended for web UI only!
82
+ * @returns Configuration settings
83
+ * @example
84
+ * ```typescript
85
+ * const config = await mailpit.getConfiguration();
86
+ * ```
87
+ */
44
88
  async getConfiguration() {
45
89
  return await this.handleRequest(() => this.axiosInstance.get("/api/v1/webui"));
46
90
  }
47
- // Message
91
+ /**
92
+ * Retrieves a summary of a specific message and marks it as read.
93
+ * @param id - The message database ID. Defaults to `latest` to return the latest message.
94
+ * @returns Message summary
95
+ * @example
96
+ * ```typescript
97
+ * const message = await mailpit.getMessageSummary();
98
+ * ```
99
+ */
48
100
  async getMessageSummary(id = "latest") {
49
101
  return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}`));
50
102
  }
103
+ /**
104
+ * Retrieves the headers of a specific message.
105
+ * @remarks Header keys are returned alphabetically.
106
+ * @param id - The message database ID. Defaults to `latest` to return the latest message.
107
+ * @returns Message headers
108
+ * @example
109
+ * ```typescript
110
+ * const headers = await mailpit.getMessageHeaders();
111
+ * ```
112
+ */
51
113
  async getMessageHeaders(id = "latest") {
52
114
  return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/headers`));
53
115
  }
116
+ /**
117
+ * Retrieves a specific attachment from a message.
118
+ * @param id - Message database ID or "latest"
119
+ * @param partID - The attachment part ID
120
+ * @returns Attachment as binary data and the content type
121
+ * @example
122
+ * ```typescript
123
+ * const message = await mailpit.getMessageSummary();
124
+ * if (message.Attachments.length) {
125
+ * const attachment = await mailpit.getMessageAttachment(message.ID, message.Attachments[0].PartID);
126
+ * // Do something with the attachment data
127
+ * }
128
+ * ```
129
+ */
54
130
  async getMessageAttachment(id, partID) {
55
131
  const response = await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/part/${partID}`, { responseType: "arraybuffer" }), { fullResponse: true });
56
132
  return {
@@ -58,9 +134,24 @@ export class MailpitClient {
58
134
  contentType: response.headers["content-type"],
59
135
  };
60
136
  }
61
- async getMessageSource(id = "latest") {
62
- return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/raw`));
63
- }
137
+ /**
138
+ * Generates a cropped 180x120 JPEG thumbnail of an image attachment from a message.
139
+ * Only image attachments are supported.
140
+ * @remarks
141
+ * If the image is smaller than 180x120 then the image is padded.
142
+ * If the attachment is not an image then a blank image is returned.
143
+ * @param id - Message database ID or "latest"
144
+ * @param partID - The attachment part ID
145
+ * @returns Image attachment thumbnail as binary data and the content type
146
+ * @example
147
+ * ```typescript
148
+ * const message = await mailpit.getMessageSummary();
149
+ * if (message.Attachments.length) {
150
+ * const thumbnail = await mailpit.getAttachmentThumbnail(message.ID, message.Attachments[0].PartID);
151
+ * // Do something with the thumbnail data
152
+ * }
153
+ * ```
154
+ */
64
155
  async getAttachmentThumbnail(id, partID) {
65
156
  const response = await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/part/${partID}/thumb`, {
66
157
  responseType: "arraybuffer",
@@ -70,72 +161,304 @@ export class MailpitClient {
70
161
  contentType: response.headers["content-type"],
71
162
  };
72
163
  }
73
- async releaseMessage(id, releaseRequest) {
74
- return await this.handleRequest(() => this.axiosInstance.post(`/api/v1/message/${id}/release`, releaseRequest));
164
+ /**
165
+ * Retrieves the full email message source as plain text.
166
+ * @param id - The message database ID. Defaults to `latest` to return the latest message.
167
+ * @returns Plain text message source
168
+ * @example
169
+ * ```typescript
170
+ * const messageSource = await mailpit.getMessageSource();
171
+ * ```
172
+ */
173
+ async getMessageSource(id = "latest") {
174
+ return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/raw`));
175
+ }
176
+ /**
177
+ * Release a message via a pre-configured external SMTP server.
178
+ * @remarks This is only enabled if message relaying has been configured.
179
+ * @param id - The message database ID. Use `latest` to return the latest message.
180
+ * @param relayTo - Array of email addresses to relay the message to
181
+ * @returns Plain text "ok" response
182
+ * @example
183
+ * ```typescript
184
+ * const message = await mailpit.releaseMessage("latest", ["user1@example.test", "user2@example.test"]);
185
+ * ```
186
+ */
187
+ async releaseMessage(id, relayTo) {
188
+ return await this.handleRequest(() => this.axiosInstance.post(`/api/v1/message/${id}/release`, relayTo));
75
189
  }
190
+ /**
191
+ * Sends a message
192
+ * @param sendReqest - The request containing the message details.
193
+ * @returns Response containing database messsage ID
194
+ * @example
195
+ * ```typescript
196
+ * await mailpit.sendMessage(
197
+ * From: { Email: "user@example.test", Name: "First LastName" },
198
+ * To: [{ Email: "rec@example.test", Name: "Recipient Name"}, {Email: "another@example.test"}],
199
+ * Subject: "Test Email",
200
+ * );
201
+ * ```
202
+ */
76
203
  async sendMessage(sendReqest) {
77
204
  return await this.handleRequest(() => this.axiosInstance.post(`/api/v1/send`, sendReqest));
78
205
  }
79
- // Other
80
- async htmlCheck(id = "latest") {
81
- return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/html-check`));
82
- }
83
- async linkCheck(id = "latest", follow = "false") {
84
- return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/link-check`, { params: { follow } }));
85
- }
86
- async spamAssassinCheck(id = "latest") {
87
- return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/sa-check`));
88
- }
89
- // Messages
206
+ /**
207
+ * Retrieves a list of message summaries ordered from newest to oldest.
208
+ * @remarks Only contains the number of attachments and a snippet of the message body.
209
+ * @see {@link MailpitClient.getMessageSummary | getMessageSummary()} for more attachment and body details for a specific message.
210
+ * @param start - The pagination offset. Defaults to `0`.
211
+ * @param limit - The number of messages to retrieve. Defaults to `50`.
212
+ * @returns A list of message summaries
213
+ * @example
214
+ * ```typescript
215
+ * const messages = await.listMessages();
216
+ * ```
217
+ */
90
218
  async listMessages(start = 0, limit = 50) {
91
219
  return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/messages`, { params: { start, limit } }));
92
220
  }
93
- async setReadStatus(readStatus) {
94
- return await this.handleRequest(() => this.axiosInstance.put(`/api/v1/messages`, readStatus));
221
+ /**
222
+ * Set the read status of messages.
223
+ * @remarks You can optionally provide an array of `IDs` **OR** a `Search` filter. If neither is set then all messages are updated.
224
+ * @param readStatus - The request containing the message database IDs/search string and the read status.
225
+ * @param readStatus.Read - The read status to set. Defaults to `false`.
226
+ * @param readStatus.IDs - The optional IDs of the messages to update.
227
+ * @param readStatus.Search - The optional search string to filter messages.
228
+ * @param params - Optional parameters for defining the time zone when using the `before:` and `after:` search filters.
229
+ * @see {@link https://mailpit.axllent.org/docs/usage/search-filters/ | Search filters}
230
+ * @returns Plain text "ok" response
231
+ * @example
232
+ * ```typescript
233
+ * // Set all messages as unread
234
+ * await mailpit.setReadStatus();
235
+ *
236
+ * // Set all messages as read
237
+ * await mailpit.setReadStatus({ Read: true });
238
+ *
239
+ * // Set specific messages as read using IDs
240
+ * await mailpit.setReadStatus({ IDs: ["1", "2", "3"], Read: true });
241
+ *
242
+ * // Set specific messages as read using search
243
+ * await mailpit.setReadStatus({ Search: "from:example.test", Read: true });
244
+ *
245
+ * // Set specific messages as read using after: search with time zone
246
+ * await mailpit.setReadStatus({ Search: "after:2025-04-30", Read: true }, { tz: "America/Chicago" });
247
+ * ```
248
+ */
249
+ async setReadStatus(readStatus, params) {
250
+ return await this.handleRequest(() => this.axiosInstance.put(`/api/v1/messages`, readStatus, {
251
+ params,
252
+ }));
95
253
  }
254
+ /**
255
+ * Delete individual or all messages.
256
+ * @remarks If no `IDs` are provided then all messages are deleted.
257
+ * @param deleteRequest - The request containing the message database IDs to delete.
258
+ * @returns Plain text "ok" response
259
+ * @example
260
+ * ```typescript
261
+ * // Delete all messages
262
+ * await mailpit.deleteMessages();
263
+ *
264
+ * // Delete specific messages
265
+ * await mailpit.deleteMessages({ IDs: ["1", "2", "3"] });
266
+ * ```
267
+ */
96
268
  async deleteMessages(deleteRequest) {
97
269
  return await this.handleRequest(() => this.axiosInstance.delete(`/api/v1/messages`, {
98
270
  data: deleteRequest,
99
271
  }));
100
272
  }
101
- // See https://mailpit.axllent.org/docs/usage/search-filters/
273
+ /**
274
+ * Retrieve messages matching a search, sorted by received date (descending).
275
+ * @see {@link https://mailpit.axllent.org/docs/usage/search-filters/ | Search filters}
276
+ * @remarks Only contains the number of attachments and a snippet of the message body.
277
+ * @see {@link MailpitClient.getMessageSummary | getMessageSummary()} for more attachment and body details for a specific message.
278
+ * @param search - The search request containing the query and optional parameters.
279
+ * @returns A list of message summaries matching the search criteria.
280
+ * @example
281
+ * ```typescript
282
+ * // Search for messages from a the domain example.test
283
+ * const messages = await mailpit.searchMessages({query: "from:example.test"});
284
+ * ```
285
+ */
102
286
  async searchMessages(search) {
103
287
  return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/search`, {
104
288
  params: search,
105
289
  }));
106
290
  }
107
- // See https://mailpit.axllent.org/docs/usage/search-filters/
291
+ /**
292
+ * Delete all messages matching a search.
293
+ * @see {@link https://mailpit.axllent.org/docs/usage/search-filters/ | Search filters}
294
+ * @param search - The search request containing the query.
295
+ * @returns Plain text "ok" response
296
+ * @example
297
+ * ```typescript
298
+ * // Delete all messages from the domain example.test
299
+ * await mailpit.deleteMessagesBySearch({query: "from:example.test"});
300
+ * ```
301
+ */
108
302
  async deleteMessagesBySearch(search) {
109
303
  return await this.handleRequest(() => this.axiosInstance.delete(`/api/v1/search`, { params: search }));
110
304
  }
111
- // Tags
305
+ /**
306
+ * Performs an HTML check on a specific message.
307
+ * @param id - The message database ID. Defaults to `latest` to return the latest message.
308
+ * @returns The summary of the message HTML checker
309
+ * @example
310
+ * ```typescript
311
+ * const htmlCheck = await mailpit.htmlCheck();
312
+ * ```
313
+ */
314
+ async htmlCheck(id = "latest") {
315
+ return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/html-check`));
316
+ }
317
+ /**
318
+ * Performs a link check on a specific message.
319
+ * @param id - The message database ID. Defaults to `latest` to return the latest message.
320
+ * @param follow - Whether to follow links. Defaults to `false`.
321
+ * @returns The summary of the message Link checker.
322
+ * @example
323
+ * ```typescript
324
+ * const linkCheck = await mailpit.linkCheck();
325
+ * ```
326
+ */
327
+ async linkCheck(id = "latest", follow = "false") {
328
+ return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/link-check`, { params: { follow } }));
329
+ }
330
+ /**
331
+ * Performs a SpamAssassin check (if enabled) on a specific message.
332
+ * @param id - The message database ID. Defaults to `latest` to return the latest message.
333
+ * @returns The SpamAssassin summary (if enabled)
334
+ * @example
335
+ * ```typescript
336
+ * const spamAssassinCheck = await mailpit.spamAssassinCheck();
337
+ * ```
338
+ */
339
+ async spamAssassinCheck(id = "latest") {
340
+ return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/message/${id}/sa-check`));
341
+ }
342
+ /**
343
+ * Retrieves a list of all the unique tags.
344
+ * @returns All unique message tags
345
+ * @example
346
+ * ```typescript
347
+ * const tags = await mailpit.getTags();
348
+ * ```
349
+ */
112
350
  async getTags() {
113
351
  return await this.handleRequest(() => this.axiosInstance.get(`/api/v1/tags`));
114
352
  }
353
+ /**
354
+ * Sets and removes tag(s) on message(s). This will overwrite any existing tags for selected message database IDs.
355
+ * @param request - The request containing the message IDs and tags. To remove all tags from a message, pass an empty `Tags` array or exclude `Tags` entirely.
356
+ * @remarks
357
+ * Tags are limited to the following characters: `a-z`, `A-Z`, `0-9`, `-`, `.`, `spaces`, and `_`, and must be a minimum of 1 character.
358
+ * Other characters are silently stripped from the tag.
359
+ * @returns Plain text "ok" response
360
+ * @example
361
+ * ```typescript
362
+ * // Set tags on message(s)
363
+ * await mailpit.setTags({ IDs: ["1", "2", "3"], Tags: ["tag1", "tag2"] });
364
+ * // Remove tags from message(s)
365
+ * await mailpit.setTags({ IDs: ["1", "2", "3"]});
366
+ * ```
367
+ */
115
368
  async setTags(request) {
116
369
  return await this.handleRequest(() => this.axiosInstance.put(`/api/v1/tags`, request));
117
370
  }
371
+ /**
372
+ * Renames an existing tag.
373
+ * @param tag - The current name of the tag.
374
+ * @param newTagName - A new name for the tag.
375
+ * @remarks
376
+ * Tags are limited to the following characters: `a-z`, `A-Z`, `0-9`, `-`, `.`, `spaces`, and `_`, and must be a minimum of 1 character.
377
+ * Other characters are silently stripped from the tag.
378
+ * @returns Plain text "ok" response
379
+ * @example
380
+ * ```typescript
381
+ * await mailpit.renameTag("Old Tag Name", "New Tag Name");
382
+ * ```
383
+ */
118
384
  async renameTag(tag, newTagName) {
119
- const encodedTag = encodeURI(tag);
385
+ const encodedTag = encodeURIComponent(tag);
120
386
  return await this.handleRequest(() => this.axiosInstance.put(`/api/v1/tags/${encodedTag}`, {
121
387
  Name: newTagName,
122
388
  }));
123
389
  }
390
+ /**
391
+ * Deletes a tag from all messages.
392
+ * @param tag - The name of the tag to delete.
393
+ * @remarks This does NOT delete any messages
394
+ * @returns Plain text "ok" response
395
+ * ```typescript
396
+ * await mailpit.deleteTag("Tag 1");
397
+ * ```
398
+ */
124
399
  async deleteTag(tag) {
125
- const encodedTag = encodeURI(tag);
400
+ const encodedTag = encodeURIComponent(tag);
126
401
  return await this.handleRequest(() => this.axiosInstance.delete(`/api/v1/tags/${encodedTag}`));
127
402
  }
128
- // Testing
129
- async renderMessageHTML(id = "latest", embed) {
130
- return await this.handleRequest(() => this.axiosInstance.get(`/view/${id}.html`, { params: { embed } }));
131
- }
132
- async renderMessageText(id = "latest") {
133
- return await this.handleRequest(() => this.axiosInstance.get(`/view/${id}.txt`));
134
- }
403
+ /**
404
+ * Retrieves the current Chaos triggers configuration (if enabled).
405
+ * @remarks This will return an error if Chaos is not enabled at runtime.
406
+ * @returns The Chaos triggers configuration
407
+ * @example
408
+ * ```typescript
409
+ * const triggers = await mailpit.getChaosTriggers();
410
+ * ```
411
+ */
135
412
  async getChaosTriggers() {
136
413
  return await this.handleRequest(() => this.axiosInstance.get("/api/v1/chaos"));
137
414
  }
415
+ /**
416
+ * Sets and/or resets the Chaos triggers configuration (if enabled).
417
+ * @param triggers - The request containing the chaos triggers. Omitted triggers will reset to the default `0%` probabibility.
418
+ * @remarks This will return an error if Chaos is not enabled at runtime.
419
+ * @returns The updated Chaos triggers configuration
420
+ * @example
421
+ * ```typescript
422
+ * // Reset all triggers to `0%` probability
423
+ * const triggers = await mailpit.setChaosTriggers();
424
+ * // Set `Sender` and reset `Authentication` and `Recipient` triggers
425
+ * const triggers = await mailpit.setChaosTriggers({ Sender: { ErrorCode: 451, Probability: 5 } });
426
+ * ```
427
+ */
138
428
  async setChaosTriggers(triggers = {}) {
139
429
  return await this.handleRequest(() => this.axiosInstance.put("/api/v1/chaos", triggers));
140
430
  }
431
+ /**
432
+ * Renders the HTML part of a specific message which can be used for UI integration testing.
433
+ * @remarks
434
+ * Attached inline images are modified to link to the API provided they exist.
435
+ * If the message does not contain an HTML part then a 404 error is returned.
436
+ *
437
+ *
438
+ * @param id - The message database ID. Defaults to `latest` to return the latest message.
439
+ * @param embed - Whether this route is to be embedded in an iframe. Defaults to `undefined`. Set to `1` to embed.
440
+ * The `embed` parameter will add `target="_blank"` and `rel="noreferrer noopener"` to all links.
441
+ * In addition, a small script will be added to the end of the document to post (postMessage()) the height of the document back to the parent window for optional iframe height resizing.
442
+ * Note that this will also transform the message into a full HTML document (if it isn't already), so this option is useful for viewing but not programmatic testing.
443
+ * @returns Rendered HTML
444
+ * @example
445
+ * ```typescript
446
+ * const html = await mailpit.renderMessageHTML();
447
+ * ```
448
+ */
449
+ async renderMessageHTML(id = "latest", embed) {
450
+ return await this.handleRequest(() => this.axiosInstance.get(`/view/${id}.html`, { params: { embed } }));
451
+ }
452
+ /**
453
+ * Renders just the message's text part which can be used for UI integration testing.
454
+ * @param id - The message database ID. Defaults to `latest` to return the latest message.
455
+ * @returns Plain text
456
+ * @example
457
+ * ```typescript
458
+ * const html = await mailpit.renderMessageText();
459
+ * ```
460
+ */
461
+ async renderMessageText(id = "latest") {
462
+ return await this.handleRequest(() => this.axiosInstance.get(`/view/${id}.txt`));
463
+ }
141
464
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailpit-api",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "A NodeJS client library, written in TypeScript, to interact with the Mailpit API.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,7 +18,8 @@
18
18
  "test": "echo \"TODO: Add tests\" && exit 0",
19
19
  "build": "rm -fr dist/* && tsc -p tsconfig.json && tsc -p tsconfig-cjs.json && ./fixup_type",
20
20
  "pretty": "npx prettier . --write",
21
- "lint": "npx eslint --fix src"
21
+ "lint": "npx eslint --fix src",
22
+ "docs": "typedoc --readme none"
22
23
  },
23
24
  "keywords": [
24
25
  "mailpit-api",
@@ -34,17 +35,20 @@
34
35
  "author": "Matthew Spahr",
35
36
  "license": "MIT",
36
37
  "dependencies": {
37
- "axios": "^1.7.9"
38
+ "axios": "^1.8.4"
38
39
  },
39
40
  "devDependencies": {
40
- "@eslint/js": "^8.57.1",
41
- "@types/eslint__js": "^8.42.3",
42
- "@types/node": "^20.14.15",
43
- "eslint": "^9.20.1",
41
+ "@eslint/js": "^9.25.1",
42
+ "@types/eslint__js": "^8.31.1",
43
+ "@types/node": "^22.15.3",
44
+ "eslint": "^9.25.1",
44
45
  "jest": "^29.7.0",
45
- "prettier": "3.4.2",
46
- "tsx": "^4.19.3",
47
- "typescript": "^5.7.3",
48
- "typescript-eslint": "^8.24.0"
46
+ "prettier": "3.5.3",
47
+ "tsx": "^4.19.4",
48
+ "typedoc": "^0.28.3",
49
+ "typedoc-github-wiki-theme": "^2.1.0",
50
+ "typedoc-plugin-markdown": "^4.6.3",
51
+ "typescript": "^5.8.3",
52
+ "typescript-eslint": "^8.31.1"
49
53
  }
50
54
  }