mailpit-api 1.5.4 → 1.7.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/README.md CHANGED
@@ -61,6 +61,7 @@ export const test = base.extend<MyFixtures>({
61
61
  const mailpit = new MailpitClient("http://localhost:8025");
62
62
  await mailpit.deleteMessages();
63
63
  await use(mailpit);
64
+ mailpit.disconnect();
64
65
  },
65
66
  });
66
67
 
@@ -75,7 +76,10 @@ test("should receive welcome email after registration", async ({
75
76
  page,
76
77
  mailpit,
77
78
  }) => {
78
- // Register
79
+ // Start waiting for the new email event before triggering the action
80
+ const emailPromise = mailpit.waitForEvent("new");
81
+
82
+ // Register a new user
79
83
  await page.goto("/register");
80
84
  await page.getByTestId("email").fill("test@example.test");
81
85
  await page.getByTestId("password").fill("password123");
@@ -84,12 +88,12 @@ test("should receive welcome email after registration", async ({
84
88
  // Wait for success message on page
85
89
  await expect(page.getByTestId("success-message")).toBeVisible();
86
90
 
87
- // Get the welcome email
88
- const message = await mailpit.getMessageSummary();
91
+ // Wait for the new email event (up to 5 seconds by default)
92
+ const event = await emailPromise;
89
93
 
90
- expect(message.To[0].Address).toBe("test@example.test");
91
- expect(message.From.Address).toBe("no-reply@your-app.test");
92
- expect(message.Subject).toBe("Welcome to Our App");
93
- expect(message.Text).toContain("Thank you for registering with our app!");
94
+ // Verify the email from the event data
95
+ expect(event.Data.To[0].Address).toBe("test@example.test");
96
+ expect(event.Data.From.Address).toBe("no-reply@your-app.test");
97
+ expect(event.Data.Subject).toBe("Welcome to Our App");
94
98
  });
95
99
  ```
package/dist/index.d.mts CHANGED
@@ -35,6 +35,15 @@ interface MailpitAttachmentResponse {
35
35
  PartID: string;
36
36
  /** Size in bytes */
37
37
  Size: number;
38
+ /** Checksums for the attachment */
39
+ Checksums: {
40
+ /** MD5 checksum */
41
+ MD5: string;
42
+ /** SHA1 checksum */
43
+ SHA1: string;
44
+ /** SHA256 checksum */
45
+ SHA256: string;
46
+ };
38
47
  }
39
48
  /** Represents information about a Chaos trigger */
40
49
  interface MailpitChaosTrigger {
@@ -177,38 +186,7 @@ interface MailpitMessageSummaryResponse {
177
186
  /** Response for the {@link MailpitClient.listMessages| listMessages()} API containing the summary of multiple messages. */
178
187
  interface MailpitMessagesSummaryResponse {
179
188
  /** Messages */
180
- messages: {
181
- /** The number of attachments */
182
- Attachments: number;
183
- /** BCC addresses */
184
- Bcc: MailpitEmailAddressResponse[];
185
- /** CC addresses */
186
- Cc: MailpitEmailAddressResponse[];
187
- /** Created time in ISO format: 1970-01-01T00:00:00.000Z */
188
- Created: string;
189
- /** Sender address */
190
- From: MailpitEmailAddressResponse;
191
- /** Database ID */
192
- ID: string;
193
- /** Message ID */
194
- MessageID: string;
195
- /** Read status */
196
- Read: boolean;
197
- /** Reply-To addresses */
198
- ReplyTo: MailpitEmailAddressResponse[];
199
- /** Message size in bytes (total) */
200
- Size: number;
201
- /** Message snippet includes up to 250 characters */
202
- Snippet: string;
203
- /** Email subject */
204
- Subject: string;
205
- /** Message tags */
206
- Tags: string[];
207
- /** To addresses */
208
- To: MailpitEmailAddressResponse[];
209
- /** Username used for authentication (if provided) with the SMTP or {@link MailpitClient.sendMessage| sendMessage} */
210
- Username?: string;
211
- }[];
189
+ messages: MailpitMessageListItem[];
212
190
  /** Total number of messages matching the current query */
213
191
  messages_count: number;
214
192
  /** Total number of unread messages matching current query */
@@ -224,6 +202,39 @@ interface MailpitMessagesSummaryResponse {
224
202
  /** @deprecated No longer documented upstream */
225
203
  count: number;
226
204
  }
205
+ /** Represents a message item in a list or WebSocket event */
206
+ interface MailpitMessageListItem {
207
+ /** The number of attachments */
208
+ Attachments: number;
209
+ /** BCC addresses */
210
+ Bcc: MailpitEmailAddressResponse[];
211
+ /** CC addresses */
212
+ Cc: MailpitEmailAddressResponse[];
213
+ /** Created time in ISO format: 1970-01-01T00:00:00.000Z */
214
+ Created: string;
215
+ /** Sender address */
216
+ From: MailpitEmailAddressResponse;
217
+ /** Database ID */
218
+ ID: string;
219
+ /** Message ID */
220
+ MessageID: string;
221
+ /** Read status */
222
+ Read: boolean;
223
+ /** Reply-To addresses */
224
+ ReplyTo: MailpitEmailAddressResponse[];
225
+ /** Message size in bytes (total) */
226
+ Size: number;
227
+ /** Message snippet includes up to 250 characters */
228
+ Snippet: string;
229
+ /** Email subject */
230
+ Subject: string;
231
+ /** Message tags */
232
+ Tags: string[];
233
+ /** To addresses */
234
+ To: MailpitEmailAddressResponse[];
235
+ /** Username used for authentication (if provided) with the SMTP or {@link MailpitClient.sendMessage| sendMessage} */
236
+ Username?: string;
237
+ }
227
238
  /** Response for the {@link MailpitClient.getMessageHeaders | getMessageHeaders()} API containing message headers */
228
239
  interface MailpitMessageHeadersResponse {
229
240
  /** Message headers */
@@ -411,6 +422,91 @@ interface MailpitAttachmentDataResponse {
411
422
  /** The attachment MIME type */
412
423
  contentType: string;
413
424
  }
425
+ /** Message summary data structure returned in "new" events */
426
+ type MailpitMessageSummary = MailpitMessageListItem;
427
+ /** Statistics data structure returned in "stats" events */
428
+ interface MailpitStatsData {
429
+ /** Total number of messages in the database */
430
+ Total: number;
431
+ /** Total number of unread messages */
432
+ Unread: number;
433
+ /** Mailpit version */
434
+ Version: string;
435
+ }
436
+ /** Update data structure returned in "update" events */
437
+ interface MailpitUpdateData {
438
+ /** Message database ID */
439
+ ID: string;
440
+ /** Read status (if changed) */
441
+ Read?: boolean;
442
+ /** Tags (if changed) */
443
+ Tags?: string[];
444
+ }
445
+ /** Delete data structure returned in "delete" events */
446
+ interface MailpitDeleteData {
447
+ /** Message database ID */
448
+ ID: string;
449
+ }
450
+ /** Error data structure returned in "error" events */
451
+ interface MailpitErrorData {
452
+ /** Error severity level */
453
+ Level: string;
454
+ /** Error type */
455
+ Type: string;
456
+ /** Client IP address */
457
+ IP: string;
458
+ /** Error message */
459
+ Message: string;
460
+ }
461
+ /** Event message containing a type and data payload */
462
+ interface MailpitEvent<T = MailpitMessageSummary | MailpitStatsData | MailpitUpdateData | MailpitDeleteData | MailpitErrorData | null> {
463
+ /** Type of event being broadcast */
464
+ Type: string;
465
+ /** Event data payload */
466
+ Data: T;
467
+ }
468
+ /** Event for new messages */
469
+ interface MailpitNewMessageEvent extends MailpitEvent<MailpitMessageSummary> {
470
+ Type: "new";
471
+ }
472
+ /** Event for statistics updates */
473
+ interface MailpitStatsEvent extends MailpitEvent<MailpitStatsData> {
474
+ Type: "stats";
475
+ }
476
+ /** Event for message updates */
477
+ interface MailpitUpdateEvent extends MailpitEvent<MailpitUpdateData> {
478
+ Type: "update";
479
+ }
480
+ /** Event for message deletion */
481
+ interface MailpitDeleteEvent extends MailpitEvent<MailpitDeleteData> {
482
+ Type: "delete";
483
+ }
484
+ /** Event for database pruning (Data is null) */
485
+ interface MailpitPruneEvent extends MailpitEvent<null> {
486
+ Type: "prune";
487
+ }
488
+ /** Event for truncating all messages (Data is null) */
489
+ interface MailpitTruncateEvent extends MailpitEvent<null> {
490
+ Type: "truncate";
491
+ }
492
+ /** Event for client errors (SMTP/POP3 errors) */
493
+ interface MailpitErrorEvent extends MailpitEvent<MailpitErrorData> {
494
+ Type: "error";
495
+ }
496
+ /** Maps event type strings to their corresponding event interfaces */
497
+ interface MailpitEventMap {
498
+ new: MailpitNewMessageEvent;
499
+ stats: MailpitStatsEvent;
500
+ update: MailpitUpdateEvent;
501
+ delete: MailpitDeleteEvent;
502
+ prune: MailpitPruneEvent;
503
+ truncate: MailpitTruncateEvent;
504
+ error: MailpitErrorEvent;
505
+ /** Wildcard event type that matches all events */
506
+ "*": MailpitEvent;
507
+ }
508
+ /** Valid event types including specific event names and the wildcard "*" */
509
+ type MailpitEventType = keyof MailpitEventMap;
414
510
  /**
415
511
  * Client for interacting with the {@link https://mailpit.axllent.org/docs/api-v1/ | Mailpit API}.
416
512
  * @example
@@ -422,6 +518,10 @@ interface MailpitAttachmentDataResponse {
422
518
  */
423
519
  declare class MailpitClient {
424
520
  private readonly axiosInstance;
521
+ private readonly baseURL;
522
+ private readonly wsURL;
523
+ private webSocket;
524
+ private eventListeners;
425
525
  /**
426
526
  * Creates an instance of {@link MailpitClient}.
427
527
  * @param baseURL - The base URL of the Mailpit API.
@@ -435,8 +535,8 @@ declare class MailpitClient {
435
535
  * @example Basic Auth
436
536
  * ```typescript
437
537
  * const mailpit = new MailpitClient("http://localhost:8025", {
438
- * username: "admin",
439
- * password: "supersecret",
538
+ * username: "admin",
539
+ * password: "supersecret"
440
540
  * });
441
541
  * ```
442
542
  */
@@ -781,6 +881,110 @@ declare class MailpitClient {
781
881
  * ```
782
882
  */
783
883
  renderMessageText(id?: string): Promise<string>;
884
+ /**
885
+ * @internal
886
+ * Connects to the WebSocket endpoint for receiving real-time events.
887
+ */
888
+ private connectWebSocket;
889
+ /**
890
+ * @internal
891
+ * Adds a listener to the event listeners map.
892
+ * @param eventType - The type of event to listen for
893
+ * @param listener - The listener function to add
894
+ */
895
+ private addListener;
896
+ /**
897
+ * @internal
898
+ * Removes a listener from the event listeners map.
899
+ * @param eventType - The type of event to remove the listener from
900
+ * @param listener - The listener function to remove
901
+ */
902
+ private removeListener;
903
+ /**
904
+ * @internal
905
+ * Dispatches a message to listeners of a specific event type.
906
+ * @param eventType - The event type to dispatch to
907
+ * @param message - The event message
908
+ */
909
+ private dispatchToListeners;
910
+ /**
911
+ * @internal
912
+ * Handles incoming WebSocket messages and dispatches them to registered listeners.
913
+ * @param message - The event message
914
+ */
915
+ private handleWebSocketMessage;
916
+ /**
917
+ * Disconnects from the real-time event stream.
918
+ * @example
919
+ * ```typescript
920
+ * mailpit.disconnect();
921
+ * ```
922
+ */
923
+ disconnect(): void;
924
+ /**
925
+ * Registers a listener for real-time events of a specific type.
926
+ * @remarks
927
+ * Automatically connects to the event stream if not already connected.
928
+ * @param eventType - The type of event to listen for.
929
+ * Specific event types include: "new" (new messages), "stats", "update", "delete", "prune", "truncate", and "error".
930
+ * Use "*" to listen for all event types (Useful if processing all events uniformly (e.g., logging, debugging, metrics)).
931
+ * @param listener - The callback function to invoke when an event is received
932
+ * @returns A function to unregister the listener
933
+ * @example Listen for event type "new" messages (recommended)
934
+ * ```typescript
935
+ * const unsubscribe = mailpit.onEvent("new", (event) => {
936
+ * // event.Data is typed as MailpitMessageSummary with full type safety
937
+ * console.log("New message:", event.Data.Subject);
938
+ * });
939
+ *
940
+ * // Other code...
941
+ *
942
+ * // Unsubscribe listener when no longer needed
943
+ * unsubscribe();
944
+ * ```
945
+ * @example Listen for all events uniformly (for logging/debugging)
946
+ * ```typescript
947
+ * const unsubscribe = mailpit.onEvent("*", (event) => {
948
+ * // Generic processing for all event types
949
+ * console.log(`Event ${event.Type} received`);
950
+ * });
951
+ *
952
+ * // Other code...
953
+ *
954
+ * // Unsubscribe listener when no longer needed
955
+ * unsubscribe();
956
+ * ```
957
+ */
958
+ onEvent<T extends keyof MailpitEventMap>(eventType: T, listener: (event: MailpitEventMap[T]) => void): () => void;
959
+ /**
960
+ * Waits for the next event of a specific type.
961
+ * @remarks
962
+ * Automatically connects to the event stream if not already connected.
963
+ * Primarily intended for testing scenarios where you need to wait for a single specific event.
964
+ * The promise will reject if the timeout is reached before an event is received.
965
+ * @param eventType - The type of event to wait for.
966
+ * Specific event types include: "new" (new messages), "stats", "update", "delete", "prune", "truncate", and "error".
967
+ * @param timeout - Timeout in milliseconds (default: 5000ms). Pass `Infinity` to disable timeout.
968
+ * @returns A promise that resolves with the event when received, or rejects on timeout
969
+ * @example Basic usage
970
+ * ```typescript
971
+ * // Create the promise before triggering the event
972
+ * const eventPromise = mailpit.waitForEvent("new");
973
+ *
974
+ * // Do something that triggers an email to send
975
+ * await mailpit.sendMessage({
976
+ * From: { Email: "test@example.com" },
977
+ * To: [{ Email: "recipient@example.com" }],
978
+ * Subject: "Test",
979
+ * });
980
+ *
981
+ * // Wait for the event confirming the message was received
982
+ * const event = await eventPromise;
983
+ * // event.Data is fully typed as MailpitMessageSummary
984
+ * console.log("Message received:", event.Data.Subject);
985
+ * ```
986
+ */
987
+ waitForEvent<T extends Exclude<keyof MailpitEventMap, "*">>(eventType: T, timeout?: number): Promise<MailpitEventMap[T]>;
784
988
  }
785
989
 
786
- export { type MailpitAttachmentDataResponse, type MailpitAttachmentRequest, type MailpitAttachmentResponse, type MailpitChaosTrigger, type MailpitChaosTriggersRequest, type MailpitChaosTriggersResponse, MailpitClient, type MailpitConfigurationResponse, type MailpitDatabaseIDsRequest, type MailpitEmailAddressRequest, type MailpitEmailAddressResponse, type MailpitHTMLCheckResponse, type MailpitInfoResponse, type MailpitLinkCheckResponse, type MailpitMessageHeadersResponse, type MailpitMessageSummaryResponse, type MailpitMessagesSummaryResponse, type MailpitReadStatusRequest, type MailpitSearchMessagesRequest, type MailpitSearchRequest, type MailpitSendMessageConfirmationResponse, type MailpitSendRequest, type MailpitSetTagsRequest, type MailpitSpamAssassinResponse, type MailpitTimeZoneRequest };
990
+ export { type MailpitAttachmentDataResponse, type MailpitAttachmentRequest, type MailpitAttachmentResponse, type MailpitChaosTrigger, type MailpitChaosTriggersRequest, type MailpitChaosTriggersResponse, MailpitClient, type MailpitConfigurationResponse, type MailpitDatabaseIDsRequest, type MailpitDeleteData, type MailpitDeleteEvent, type MailpitEmailAddressRequest, type MailpitEmailAddressResponse, type MailpitErrorData, type MailpitErrorEvent, type MailpitEvent, type MailpitEventMap, type MailpitEventType, type MailpitHTMLCheckResponse, type MailpitInfoResponse, type MailpitLinkCheckResponse, type MailpitMessageHeadersResponse, type MailpitMessageListItem, type MailpitMessageSummary, type MailpitMessageSummaryResponse, type MailpitMessagesSummaryResponse, type MailpitNewMessageEvent, type MailpitPruneEvent, type MailpitReadStatusRequest, type MailpitSearchMessagesRequest, type MailpitSearchRequest, type MailpitSendMessageConfirmationResponse, type MailpitSendRequest, type MailpitSetTagsRequest, type MailpitSpamAssassinResponse, type MailpitStatsData, type MailpitStatsEvent, type MailpitTimeZoneRequest, type MailpitTruncateEvent, type MailpitUpdateData, type MailpitUpdateEvent };
package/dist/index.d.ts CHANGED
@@ -35,6 +35,15 @@ interface MailpitAttachmentResponse {
35
35
  PartID: string;
36
36
  /** Size in bytes */
37
37
  Size: number;
38
+ /** Checksums for the attachment */
39
+ Checksums: {
40
+ /** MD5 checksum */
41
+ MD5: string;
42
+ /** SHA1 checksum */
43
+ SHA1: string;
44
+ /** SHA256 checksum */
45
+ SHA256: string;
46
+ };
38
47
  }
39
48
  /** Represents information about a Chaos trigger */
40
49
  interface MailpitChaosTrigger {
@@ -177,38 +186,7 @@ interface MailpitMessageSummaryResponse {
177
186
  /** Response for the {@link MailpitClient.listMessages| listMessages()} API containing the summary of multiple messages. */
178
187
  interface MailpitMessagesSummaryResponse {
179
188
  /** Messages */
180
- messages: {
181
- /** The number of attachments */
182
- Attachments: number;
183
- /** BCC addresses */
184
- Bcc: MailpitEmailAddressResponse[];
185
- /** CC addresses */
186
- Cc: MailpitEmailAddressResponse[];
187
- /** Created time in ISO format: 1970-01-01T00:00:00.000Z */
188
- Created: string;
189
- /** Sender address */
190
- From: MailpitEmailAddressResponse;
191
- /** Database ID */
192
- ID: string;
193
- /** Message ID */
194
- MessageID: string;
195
- /** Read status */
196
- Read: boolean;
197
- /** Reply-To addresses */
198
- ReplyTo: MailpitEmailAddressResponse[];
199
- /** Message size in bytes (total) */
200
- Size: number;
201
- /** Message snippet includes up to 250 characters */
202
- Snippet: string;
203
- /** Email subject */
204
- Subject: string;
205
- /** Message tags */
206
- Tags: string[];
207
- /** To addresses */
208
- To: MailpitEmailAddressResponse[];
209
- /** Username used for authentication (if provided) with the SMTP or {@link MailpitClient.sendMessage| sendMessage} */
210
- Username?: string;
211
- }[];
189
+ messages: MailpitMessageListItem[];
212
190
  /** Total number of messages matching the current query */
213
191
  messages_count: number;
214
192
  /** Total number of unread messages matching current query */
@@ -224,6 +202,39 @@ interface MailpitMessagesSummaryResponse {
224
202
  /** @deprecated No longer documented upstream */
225
203
  count: number;
226
204
  }
205
+ /** Represents a message item in a list or WebSocket event */
206
+ interface MailpitMessageListItem {
207
+ /** The number of attachments */
208
+ Attachments: number;
209
+ /** BCC addresses */
210
+ Bcc: MailpitEmailAddressResponse[];
211
+ /** CC addresses */
212
+ Cc: MailpitEmailAddressResponse[];
213
+ /** Created time in ISO format: 1970-01-01T00:00:00.000Z */
214
+ Created: string;
215
+ /** Sender address */
216
+ From: MailpitEmailAddressResponse;
217
+ /** Database ID */
218
+ ID: string;
219
+ /** Message ID */
220
+ MessageID: string;
221
+ /** Read status */
222
+ Read: boolean;
223
+ /** Reply-To addresses */
224
+ ReplyTo: MailpitEmailAddressResponse[];
225
+ /** Message size in bytes (total) */
226
+ Size: number;
227
+ /** Message snippet includes up to 250 characters */
228
+ Snippet: string;
229
+ /** Email subject */
230
+ Subject: string;
231
+ /** Message tags */
232
+ Tags: string[];
233
+ /** To addresses */
234
+ To: MailpitEmailAddressResponse[];
235
+ /** Username used for authentication (if provided) with the SMTP or {@link MailpitClient.sendMessage| sendMessage} */
236
+ Username?: string;
237
+ }
227
238
  /** Response for the {@link MailpitClient.getMessageHeaders | getMessageHeaders()} API containing message headers */
228
239
  interface MailpitMessageHeadersResponse {
229
240
  /** Message headers */
@@ -411,6 +422,91 @@ interface MailpitAttachmentDataResponse {
411
422
  /** The attachment MIME type */
412
423
  contentType: string;
413
424
  }
425
+ /** Message summary data structure returned in "new" events */
426
+ type MailpitMessageSummary = MailpitMessageListItem;
427
+ /** Statistics data structure returned in "stats" events */
428
+ interface MailpitStatsData {
429
+ /** Total number of messages in the database */
430
+ Total: number;
431
+ /** Total number of unread messages */
432
+ Unread: number;
433
+ /** Mailpit version */
434
+ Version: string;
435
+ }
436
+ /** Update data structure returned in "update" events */
437
+ interface MailpitUpdateData {
438
+ /** Message database ID */
439
+ ID: string;
440
+ /** Read status (if changed) */
441
+ Read?: boolean;
442
+ /** Tags (if changed) */
443
+ Tags?: string[];
444
+ }
445
+ /** Delete data structure returned in "delete" events */
446
+ interface MailpitDeleteData {
447
+ /** Message database ID */
448
+ ID: string;
449
+ }
450
+ /** Error data structure returned in "error" events */
451
+ interface MailpitErrorData {
452
+ /** Error severity level */
453
+ Level: string;
454
+ /** Error type */
455
+ Type: string;
456
+ /** Client IP address */
457
+ IP: string;
458
+ /** Error message */
459
+ Message: string;
460
+ }
461
+ /** Event message containing a type and data payload */
462
+ interface MailpitEvent<T = MailpitMessageSummary | MailpitStatsData | MailpitUpdateData | MailpitDeleteData | MailpitErrorData | null> {
463
+ /** Type of event being broadcast */
464
+ Type: string;
465
+ /** Event data payload */
466
+ Data: T;
467
+ }
468
+ /** Event for new messages */
469
+ interface MailpitNewMessageEvent extends MailpitEvent<MailpitMessageSummary> {
470
+ Type: "new";
471
+ }
472
+ /** Event for statistics updates */
473
+ interface MailpitStatsEvent extends MailpitEvent<MailpitStatsData> {
474
+ Type: "stats";
475
+ }
476
+ /** Event for message updates */
477
+ interface MailpitUpdateEvent extends MailpitEvent<MailpitUpdateData> {
478
+ Type: "update";
479
+ }
480
+ /** Event for message deletion */
481
+ interface MailpitDeleteEvent extends MailpitEvent<MailpitDeleteData> {
482
+ Type: "delete";
483
+ }
484
+ /** Event for database pruning (Data is null) */
485
+ interface MailpitPruneEvent extends MailpitEvent<null> {
486
+ Type: "prune";
487
+ }
488
+ /** Event for truncating all messages (Data is null) */
489
+ interface MailpitTruncateEvent extends MailpitEvent<null> {
490
+ Type: "truncate";
491
+ }
492
+ /** Event for client errors (SMTP/POP3 errors) */
493
+ interface MailpitErrorEvent extends MailpitEvent<MailpitErrorData> {
494
+ Type: "error";
495
+ }
496
+ /** Maps event type strings to their corresponding event interfaces */
497
+ interface MailpitEventMap {
498
+ new: MailpitNewMessageEvent;
499
+ stats: MailpitStatsEvent;
500
+ update: MailpitUpdateEvent;
501
+ delete: MailpitDeleteEvent;
502
+ prune: MailpitPruneEvent;
503
+ truncate: MailpitTruncateEvent;
504
+ error: MailpitErrorEvent;
505
+ /** Wildcard event type that matches all events */
506
+ "*": MailpitEvent;
507
+ }
508
+ /** Valid event types including specific event names and the wildcard "*" */
509
+ type MailpitEventType = keyof MailpitEventMap;
414
510
  /**
415
511
  * Client for interacting with the {@link https://mailpit.axllent.org/docs/api-v1/ | Mailpit API}.
416
512
  * @example
@@ -422,6 +518,10 @@ interface MailpitAttachmentDataResponse {
422
518
  */
423
519
  declare class MailpitClient {
424
520
  private readonly axiosInstance;
521
+ private readonly baseURL;
522
+ private readonly wsURL;
523
+ private webSocket;
524
+ private eventListeners;
425
525
  /**
426
526
  * Creates an instance of {@link MailpitClient}.
427
527
  * @param baseURL - The base URL of the Mailpit API.
@@ -435,8 +535,8 @@ declare class MailpitClient {
435
535
  * @example Basic Auth
436
536
  * ```typescript
437
537
  * const mailpit = new MailpitClient("http://localhost:8025", {
438
- * username: "admin",
439
- * password: "supersecret",
538
+ * username: "admin",
539
+ * password: "supersecret"
440
540
  * });
441
541
  * ```
442
542
  */
@@ -781,6 +881,110 @@ declare class MailpitClient {
781
881
  * ```
782
882
  */
783
883
  renderMessageText(id?: string): Promise<string>;
884
+ /**
885
+ * @internal
886
+ * Connects to the WebSocket endpoint for receiving real-time events.
887
+ */
888
+ private connectWebSocket;
889
+ /**
890
+ * @internal
891
+ * Adds a listener to the event listeners map.
892
+ * @param eventType - The type of event to listen for
893
+ * @param listener - The listener function to add
894
+ */
895
+ private addListener;
896
+ /**
897
+ * @internal
898
+ * Removes a listener from the event listeners map.
899
+ * @param eventType - The type of event to remove the listener from
900
+ * @param listener - The listener function to remove
901
+ */
902
+ private removeListener;
903
+ /**
904
+ * @internal
905
+ * Dispatches a message to listeners of a specific event type.
906
+ * @param eventType - The event type to dispatch to
907
+ * @param message - The event message
908
+ */
909
+ private dispatchToListeners;
910
+ /**
911
+ * @internal
912
+ * Handles incoming WebSocket messages and dispatches them to registered listeners.
913
+ * @param message - The event message
914
+ */
915
+ private handleWebSocketMessage;
916
+ /**
917
+ * Disconnects from the real-time event stream.
918
+ * @example
919
+ * ```typescript
920
+ * mailpit.disconnect();
921
+ * ```
922
+ */
923
+ disconnect(): void;
924
+ /**
925
+ * Registers a listener for real-time events of a specific type.
926
+ * @remarks
927
+ * Automatically connects to the event stream if not already connected.
928
+ * @param eventType - The type of event to listen for.
929
+ * Specific event types include: "new" (new messages), "stats", "update", "delete", "prune", "truncate", and "error".
930
+ * Use "*" to listen for all event types (Useful if processing all events uniformly (e.g., logging, debugging, metrics)).
931
+ * @param listener - The callback function to invoke when an event is received
932
+ * @returns A function to unregister the listener
933
+ * @example Listen for event type "new" messages (recommended)
934
+ * ```typescript
935
+ * const unsubscribe = mailpit.onEvent("new", (event) => {
936
+ * // event.Data is typed as MailpitMessageSummary with full type safety
937
+ * console.log("New message:", event.Data.Subject);
938
+ * });
939
+ *
940
+ * // Other code...
941
+ *
942
+ * // Unsubscribe listener when no longer needed
943
+ * unsubscribe();
944
+ * ```
945
+ * @example Listen for all events uniformly (for logging/debugging)
946
+ * ```typescript
947
+ * const unsubscribe = mailpit.onEvent("*", (event) => {
948
+ * // Generic processing for all event types
949
+ * console.log(`Event ${event.Type} received`);
950
+ * });
951
+ *
952
+ * // Other code...
953
+ *
954
+ * // Unsubscribe listener when no longer needed
955
+ * unsubscribe();
956
+ * ```
957
+ */
958
+ onEvent<T extends keyof MailpitEventMap>(eventType: T, listener: (event: MailpitEventMap[T]) => void): () => void;
959
+ /**
960
+ * Waits for the next event of a specific type.
961
+ * @remarks
962
+ * Automatically connects to the event stream if not already connected.
963
+ * Primarily intended for testing scenarios where you need to wait for a single specific event.
964
+ * The promise will reject if the timeout is reached before an event is received.
965
+ * @param eventType - The type of event to wait for.
966
+ * Specific event types include: "new" (new messages), "stats", "update", "delete", "prune", "truncate", and "error".
967
+ * @param timeout - Timeout in milliseconds (default: 5000ms). Pass `Infinity` to disable timeout.
968
+ * @returns A promise that resolves with the event when received, or rejects on timeout
969
+ * @example Basic usage
970
+ * ```typescript
971
+ * // Create the promise before triggering the event
972
+ * const eventPromise = mailpit.waitForEvent("new");
973
+ *
974
+ * // Do something that triggers an email to send
975
+ * await mailpit.sendMessage({
976
+ * From: { Email: "test@example.com" },
977
+ * To: [{ Email: "recipient@example.com" }],
978
+ * Subject: "Test",
979
+ * });
980
+ *
981
+ * // Wait for the event confirming the message was received
982
+ * const event = await eventPromise;
983
+ * // event.Data is fully typed as MailpitMessageSummary
984
+ * console.log("Message received:", event.Data.Subject);
985
+ * ```
986
+ */
987
+ waitForEvent<T extends Exclude<keyof MailpitEventMap, "*">>(eventType: T, timeout?: number): Promise<MailpitEventMap[T]>;
784
988
  }
785
989
 
786
- export { type MailpitAttachmentDataResponse, type MailpitAttachmentRequest, type MailpitAttachmentResponse, type MailpitChaosTrigger, type MailpitChaosTriggersRequest, type MailpitChaosTriggersResponse, MailpitClient, type MailpitConfigurationResponse, type MailpitDatabaseIDsRequest, type MailpitEmailAddressRequest, type MailpitEmailAddressResponse, type MailpitHTMLCheckResponse, type MailpitInfoResponse, type MailpitLinkCheckResponse, type MailpitMessageHeadersResponse, type MailpitMessageSummaryResponse, type MailpitMessagesSummaryResponse, type MailpitReadStatusRequest, type MailpitSearchMessagesRequest, type MailpitSearchRequest, type MailpitSendMessageConfirmationResponse, type MailpitSendRequest, type MailpitSetTagsRequest, type MailpitSpamAssassinResponse, type MailpitTimeZoneRequest };
990
+ export { type MailpitAttachmentDataResponse, type MailpitAttachmentRequest, type MailpitAttachmentResponse, type MailpitChaosTrigger, type MailpitChaosTriggersRequest, type MailpitChaosTriggersResponse, MailpitClient, type MailpitConfigurationResponse, type MailpitDatabaseIDsRequest, type MailpitDeleteData, type MailpitDeleteEvent, type MailpitEmailAddressRequest, type MailpitEmailAddressResponse, type MailpitErrorData, type MailpitErrorEvent, type MailpitEvent, type MailpitEventMap, type MailpitEventType, type MailpitHTMLCheckResponse, type MailpitInfoResponse, type MailpitLinkCheckResponse, type MailpitMessageHeadersResponse, type MailpitMessageListItem, type MailpitMessageSummary, type MailpitMessageSummaryResponse, type MailpitMessagesSummaryResponse, type MailpitNewMessageEvent, type MailpitPruneEvent, type MailpitReadStatusRequest, type MailpitSearchMessagesRequest, type MailpitSearchRequest, type MailpitSendMessageConfirmationResponse, type MailpitSendRequest, type MailpitSetTagsRequest, type MailpitSpamAssassinResponse, type MailpitStatsData, type MailpitStatsEvent, type MailpitTimeZoneRequest, type MailpitTruncateEvent, type MailpitUpdateData, type MailpitUpdateEvent };
package/dist/index.js CHANGED
@@ -34,8 +34,14 @@ __export(index_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
  var import_axios = __toESM(require("axios"));
37
+ var import_partysocket = require("partysocket");
38
+ var import_ws = __toESM(require("ws"));
37
39
  var MailpitClient = class {
38
40
  axiosInstance;
41
+ baseURL;
42
+ wsURL;
43
+ webSocket = null;
44
+ eventListeners = /* @__PURE__ */ new Map();
39
45
  /**
40
46
  * Creates an instance of {@link MailpitClient}.
41
47
  * @param baseURL - The base URL of the Mailpit API.
@@ -49,12 +55,19 @@ var MailpitClient = class {
49
55
  * @example Basic Auth
50
56
  * ```typescript
51
57
  * const mailpit = new MailpitClient("http://localhost:8025", {
52
- * username: "admin",
53
- * password: "supersecret",
58
+ * username: "admin",
59
+ * password: "supersecret"
54
60
  * });
55
61
  * ```
56
62
  */
57
63
  constructor(baseURL, auth) {
64
+ if (!baseURL || !/^https?:\/\//.test(baseURL)) {
65
+ throw new Error(
66
+ "The value of the 'baseURL' parameter must start with http:// or https://"
67
+ );
68
+ }
69
+ this.baseURL = baseURL;
70
+ this.wsURL = `${baseURL.replace(/^http/, "ws")}/api/events`;
58
71
  this.axiosInstance = import_axios.default.create({
59
72
  baseURL,
60
73
  auth,
@@ -564,6 +577,198 @@ var MailpitClient = class {
564
577
  () => this.axiosInstance.get(`/view/${id}.txt`)
565
578
  );
566
579
  }
580
+ /**
581
+ * @internal
582
+ * Connects to the WebSocket endpoint for receiving real-time events.
583
+ */
584
+ connectWebSocket() {
585
+ if (this.webSocket && (this.webSocket.readyState === import_partysocket.WebSocket.OPEN || this.webSocket.readyState === import_partysocket.WebSocket.CONNECTING)) {
586
+ return;
587
+ }
588
+ const wsOptions = {};
589
+ if (this.axiosInstance.defaults.auth) {
590
+ wsOptions.headers = {
591
+ Authorization: `Basic ${Buffer.from(`${this.axiosInstance.defaults.auth.username}:${this.axiosInstance.defaults.auth.password}`).toString("base64")}`
592
+ };
593
+ }
594
+ class AuthenticatedWebSocket extends import_ws.default {
595
+ constructor(address, options) {
596
+ super(address, { ...wsOptions, ...options });
597
+ }
598
+ }
599
+ this.webSocket = new import_partysocket.WebSocket(this.wsURL, void 0, {
600
+ WebSocket: AuthenticatedWebSocket
601
+ });
602
+ this.webSocket.addEventListener("message", (event) => {
603
+ let message;
604
+ try {
605
+ message = JSON.parse(event.data);
606
+ } catch {
607
+ return;
608
+ }
609
+ this.handleWebSocketMessage(message);
610
+ });
611
+ }
612
+ /**
613
+ * @internal
614
+ * Adds a listener to the event listeners map.
615
+ * @param eventType - The type of event to listen for
616
+ * @param listener - The listener function to add
617
+ */
618
+ addListener(eventType, listener) {
619
+ if (!this.eventListeners.has(eventType)) {
620
+ this.eventListeners.set(eventType, /* @__PURE__ */ new Set());
621
+ }
622
+ this.eventListeners.get(eventType)?.add(listener);
623
+ }
624
+ /**
625
+ * @internal
626
+ * Removes a listener from the event listeners map.
627
+ * @param eventType - The type of event to remove the listener from
628
+ * @param listener - The listener function to remove
629
+ */
630
+ removeListener(eventType, listener) {
631
+ const listeners = this.eventListeners.get(eventType);
632
+ if (listeners) {
633
+ listeners.delete(listener);
634
+ if (listeners.size === 0) {
635
+ this.eventListeners.delete(eventType);
636
+ }
637
+ }
638
+ }
639
+ /**
640
+ * @internal
641
+ * Dispatches a message to listeners of a specific event type.
642
+ * @param eventType - The event type to dispatch to
643
+ * @param message - The event message
644
+ */
645
+ dispatchToListeners(eventType, message) {
646
+ const listeners = this.eventListeners.get(eventType);
647
+ if (listeners) {
648
+ listeners.forEach((listener) => {
649
+ listener(message);
650
+ });
651
+ }
652
+ }
653
+ /**
654
+ * @internal
655
+ * Handles incoming WebSocket messages and dispatches them to registered listeners.
656
+ * @param message - The event message
657
+ */
658
+ handleWebSocketMessage(message) {
659
+ this.dispatchToListeners(message.Type, message);
660
+ this.dispatchToListeners("*", message);
661
+ }
662
+ /**
663
+ * Disconnects from the real-time event stream.
664
+ * @example
665
+ * ```typescript
666
+ * mailpit.disconnect();
667
+ * ```
668
+ */
669
+ disconnect() {
670
+ if (this.webSocket) {
671
+ const ws = this.webSocket;
672
+ this.webSocket = null;
673
+ ws.close(1e3, "Client disconnect");
674
+ }
675
+ }
676
+ /**
677
+ * Registers a listener for real-time events of a specific type.
678
+ * @remarks
679
+ * Automatically connects to the event stream if not already connected.
680
+ * @param eventType - The type of event to listen for.
681
+ * Specific event types include: "new" (new messages), "stats", "update", "delete", "prune", "truncate", and "error".
682
+ * Use "*" to listen for all event types (Useful if processing all events uniformly (e.g., logging, debugging, metrics)).
683
+ * @param listener - The callback function to invoke when an event is received
684
+ * @returns A function to unregister the listener
685
+ * @example Listen for event type "new" messages (recommended)
686
+ * ```typescript
687
+ * const unsubscribe = mailpit.onEvent("new", (event) => {
688
+ * // event.Data is typed as MailpitMessageSummary with full type safety
689
+ * console.log("New message:", event.Data.Subject);
690
+ * });
691
+ *
692
+ * // Other code...
693
+ *
694
+ * // Unsubscribe listener when no longer needed
695
+ * unsubscribe();
696
+ * ```
697
+ * @example Listen for all events uniformly (for logging/debugging)
698
+ * ```typescript
699
+ * const unsubscribe = mailpit.onEvent("*", (event) => {
700
+ * // Generic processing for all event types
701
+ * console.log(`Event ${event.Type} received`);
702
+ * });
703
+ *
704
+ * // Other code...
705
+ *
706
+ * // Unsubscribe listener when no longer needed
707
+ * unsubscribe();
708
+ * ```
709
+ */
710
+ onEvent(eventType, listener) {
711
+ if (!this.webSocket || this.webSocket.readyState === import_partysocket.WebSocket.CLOSED) {
712
+ this.connectWebSocket();
713
+ }
714
+ this.addListener(eventType, listener);
715
+ return () => {
716
+ this.removeListener(eventType, listener);
717
+ };
718
+ }
719
+ /**
720
+ * Waits for the next event of a specific type.
721
+ * @remarks
722
+ * Automatically connects to the event stream if not already connected.
723
+ * Primarily intended for testing scenarios where you need to wait for a single specific event.
724
+ * The promise will reject if the timeout is reached before an event is received.
725
+ * @param eventType - The type of event to wait for.
726
+ * Specific event types include: "new" (new messages), "stats", "update", "delete", "prune", "truncate", and "error".
727
+ * @param timeout - Timeout in milliseconds (default: 5000ms). Pass `Infinity` to disable timeout.
728
+ * @returns A promise that resolves with the event when received, or rejects on timeout
729
+ * @example Basic usage
730
+ * ```typescript
731
+ * // Create the promise before triggering the event
732
+ * const eventPromise = mailpit.waitForEvent("new");
733
+ *
734
+ * // Do something that triggers an email to send
735
+ * await mailpit.sendMessage({
736
+ * From: { Email: "test@example.com" },
737
+ * To: [{ Email: "recipient@example.com" }],
738
+ * Subject: "Test",
739
+ * });
740
+ *
741
+ * // Wait for the event confirming the message was received
742
+ * const event = await eventPromise;
743
+ * // event.Data is fully typed as MailpitMessageSummary
744
+ * console.log("Message received:", event.Data.Subject);
745
+ * ```
746
+ */
747
+ waitForEvent(eventType, timeout = 5e3) {
748
+ if (!this.webSocket || this.webSocket.readyState === import_partysocket.WebSocket.CLOSED) {
749
+ this.connectWebSocket();
750
+ }
751
+ return new Promise((resolve, reject) => {
752
+ let timer = null;
753
+ const cleanup = () => {
754
+ if (timer) {
755
+ clearTimeout(timer);
756
+ }
757
+ this.removeListener(eventType, listener);
758
+ };
759
+ const listener = (event) => {
760
+ cleanup();
761
+ resolve(event);
762
+ };
763
+ this.addListener(eventType, listener);
764
+ if (isFinite(timeout)) {
765
+ timer = setTimeout(() => {
766
+ cleanup();
767
+ reject(new Error(`Timeout waiting for event of type "${eventType}"`));
768
+ }, timeout);
769
+ }
770
+ });
771
+ }
567
772
  };
568
773
  // Annotate the CommonJS export names for ESM import in node:
569
774
  0 && (module.exports = {
package/dist/index.mjs CHANGED
@@ -2,8 +2,14 @@
2
2
  import axios, {
3
3
  isAxiosError
4
4
  } from "axios";
5
+ import { WebSocket as ReconnectingWebSocket } from "partysocket";
6
+ import WS from "ws";
5
7
  var MailpitClient = class {
6
8
  axiosInstance;
9
+ baseURL;
10
+ wsURL;
11
+ webSocket = null;
12
+ eventListeners = /* @__PURE__ */ new Map();
7
13
  /**
8
14
  * Creates an instance of {@link MailpitClient}.
9
15
  * @param baseURL - The base URL of the Mailpit API.
@@ -17,12 +23,19 @@ var MailpitClient = class {
17
23
  * @example Basic Auth
18
24
  * ```typescript
19
25
  * const mailpit = new MailpitClient("http://localhost:8025", {
20
- * username: "admin",
21
- * password: "supersecret",
26
+ * username: "admin",
27
+ * password: "supersecret"
22
28
  * });
23
29
  * ```
24
30
  */
25
31
  constructor(baseURL, auth) {
32
+ if (!baseURL || !/^https?:\/\//.test(baseURL)) {
33
+ throw new Error(
34
+ "The value of the 'baseURL' parameter must start with http:// or https://"
35
+ );
36
+ }
37
+ this.baseURL = baseURL;
38
+ this.wsURL = `${baseURL.replace(/^http/, "ws")}/api/events`;
26
39
  this.axiosInstance = axios.create({
27
40
  baseURL,
28
41
  auth,
@@ -532,6 +545,198 @@ var MailpitClient = class {
532
545
  () => this.axiosInstance.get(`/view/${id}.txt`)
533
546
  );
534
547
  }
548
+ /**
549
+ * @internal
550
+ * Connects to the WebSocket endpoint for receiving real-time events.
551
+ */
552
+ connectWebSocket() {
553
+ if (this.webSocket && (this.webSocket.readyState === ReconnectingWebSocket.OPEN || this.webSocket.readyState === ReconnectingWebSocket.CONNECTING)) {
554
+ return;
555
+ }
556
+ const wsOptions = {};
557
+ if (this.axiosInstance.defaults.auth) {
558
+ wsOptions.headers = {
559
+ Authorization: `Basic ${Buffer.from(`${this.axiosInstance.defaults.auth.username}:${this.axiosInstance.defaults.auth.password}`).toString("base64")}`
560
+ };
561
+ }
562
+ class AuthenticatedWebSocket extends WS {
563
+ constructor(address, options) {
564
+ super(address, { ...wsOptions, ...options });
565
+ }
566
+ }
567
+ this.webSocket = new ReconnectingWebSocket(this.wsURL, void 0, {
568
+ WebSocket: AuthenticatedWebSocket
569
+ });
570
+ this.webSocket.addEventListener("message", (event) => {
571
+ let message;
572
+ try {
573
+ message = JSON.parse(event.data);
574
+ } catch {
575
+ return;
576
+ }
577
+ this.handleWebSocketMessage(message);
578
+ });
579
+ }
580
+ /**
581
+ * @internal
582
+ * Adds a listener to the event listeners map.
583
+ * @param eventType - The type of event to listen for
584
+ * @param listener - The listener function to add
585
+ */
586
+ addListener(eventType, listener) {
587
+ if (!this.eventListeners.has(eventType)) {
588
+ this.eventListeners.set(eventType, /* @__PURE__ */ new Set());
589
+ }
590
+ this.eventListeners.get(eventType)?.add(listener);
591
+ }
592
+ /**
593
+ * @internal
594
+ * Removes a listener from the event listeners map.
595
+ * @param eventType - The type of event to remove the listener from
596
+ * @param listener - The listener function to remove
597
+ */
598
+ removeListener(eventType, listener) {
599
+ const listeners = this.eventListeners.get(eventType);
600
+ if (listeners) {
601
+ listeners.delete(listener);
602
+ if (listeners.size === 0) {
603
+ this.eventListeners.delete(eventType);
604
+ }
605
+ }
606
+ }
607
+ /**
608
+ * @internal
609
+ * Dispatches a message to listeners of a specific event type.
610
+ * @param eventType - The event type to dispatch to
611
+ * @param message - The event message
612
+ */
613
+ dispatchToListeners(eventType, message) {
614
+ const listeners = this.eventListeners.get(eventType);
615
+ if (listeners) {
616
+ listeners.forEach((listener) => {
617
+ listener(message);
618
+ });
619
+ }
620
+ }
621
+ /**
622
+ * @internal
623
+ * Handles incoming WebSocket messages and dispatches them to registered listeners.
624
+ * @param message - The event message
625
+ */
626
+ handleWebSocketMessage(message) {
627
+ this.dispatchToListeners(message.Type, message);
628
+ this.dispatchToListeners("*", message);
629
+ }
630
+ /**
631
+ * Disconnects from the real-time event stream.
632
+ * @example
633
+ * ```typescript
634
+ * mailpit.disconnect();
635
+ * ```
636
+ */
637
+ disconnect() {
638
+ if (this.webSocket) {
639
+ const ws = this.webSocket;
640
+ this.webSocket = null;
641
+ ws.close(1e3, "Client disconnect");
642
+ }
643
+ }
644
+ /**
645
+ * Registers a listener for real-time events of a specific type.
646
+ * @remarks
647
+ * Automatically connects to the event stream if not already connected.
648
+ * @param eventType - The type of event to listen for.
649
+ * Specific event types include: "new" (new messages), "stats", "update", "delete", "prune", "truncate", and "error".
650
+ * Use "*" to listen for all event types (Useful if processing all events uniformly (e.g., logging, debugging, metrics)).
651
+ * @param listener - The callback function to invoke when an event is received
652
+ * @returns A function to unregister the listener
653
+ * @example Listen for event type "new" messages (recommended)
654
+ * ```typescript
655
+ * const unsubscribe = mailpit.onEvent("new", (event) => {
656
+ * // event.Data is typed as MailpitMessageSummary with full type safety
657
+ * console.log("New message:", event.Data.Subject);
658
+ * });
659
+ *
660
+ * // Other code...
661
+ *
662
+ * // Unsubscribe listener when no longer needed
663
+ * unsubscribe();
664
+ * ```
665
+ * @example Listen for all events uniformly (for logging/debugging)
666
+ * ```typescript
667
+ * const unsubscribe = mailpit.onEvent("*", (event) => {
668
+ * // Generic processing for all event types
669
+ * console.log(`Event ${event.Type} received`);
670
+ * });
671
+ *
672
+ * // Other code...
673
+ *
674
+ * // Unsubscribe listener when no longer needed
675
+ * unsubscribe();
676
+ * ```
677
+ */
678
+ onEvent(eventType, listener) {
679
+ if (!this.webSocket || this.webSocket.readyState === ReconnectingWebSocket.CLOSED) {
680
+ this.connectWebSocket();
681
+ }
682
+ this.addListener(eventType, listener);
683
+ return () => {
684
+ this.removeListener(eventType, listener);
685
+ };
686
+ }
687
+ /**
688
+ * Waits for the next event of a specific type.
689
+ * @remarks
690
+ * Automatically connects to the event stream if not already connected.
691
+ * Primarily intended for testing scenarios where you need to wait for a single specific event.
692
+ * The promise will reject if the timeout is reached before an event is received.
693
+ * @param eventType - The type of event to wait for.
694
+ * Specific event types include: "new" (new messages), "stats", "update", "delete", "prune", "truncate", and "error".
695
+ * @param timeout - Timeout in milliseconds (default: 5000ms). Pass `Infinity` to disable timeout.
696
+ * @returns A promise that resolves with the event when received, or rejects on timeout
697
+ * @example Basic usage
698
+ * ```typescript
699
+ * // Create the promise before triggering the event
700
+ * const eventPromise = mailpit.waitForEvent("new");
701
+ *
702
+ * // Do something that triggers an email to send
703
+ * await mailpit.sendMessage({
704
+ * From: { Email: "test@example.com" },
705
+ * To: [{ Email: "recipient@example.com" }],
706
+ * Subject: "Test",
707
+ * });
708
+ *
709
+ * // Wait for the event confirming the message was received
710
+ * const event = await eventPromise;
711
+ * // event.Data is fully typed as MailpitMessageSummary
712
+ * console.log("Message received:", event.Data.Subject);
713
+ * ```
714
+ */
715
+ waitForEvent(eventType, timeout = 5e3) {
716
+ if (!this.webSocket || this.webSocket.readyState === ReconnectingWebSocket.CLOSED) {
717
+ this.connectWebSocket();
718
+ }
719
+ return new Promise((resolve, reject) => {
720
+ let timer = null;
721
+ const cleanup = () => {
722
+ if (timer) {
723
+ clearTimeout(timer);
724
+ }
725
+ this.removeListener(eventType, listener);
726
+ };
727
+ const listener = (event) => {
728
+ cleanup();
729
+ resolve(event);
730
+ };
731
+ this.addListener(eventType, listener);
732
+ if (isFinite(timeout)) {
733
+ timer = setTimeout(() => {
734
+ cleanup();
735
+ reject(new Error(`Timeout waiting for event of type "${eventType}"`));
736
+ }, timeout);
737
+ }
738
+ });
739
+ }
535
740
  };
536
741
  export {
537
742
  MailpitClient
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "mailpit-api",
3
- "version": "1.5.4",
3
+ "version": "1.7.0",
4
4
  "description": "A TypeScript client for interacting with Mailpit's REST API.",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "https://github.com/mpspahr/mailpit-api.git"
7
+ "url": "git+https://github.com/mpspahr/mailpit-api.git"
8
8
  },
9
9
  "homepage": "https://mpspahr.github.io/mailpit-api/",
10
10
  "bugs": {
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "publishConfig": {
14
14
  "registry": "https://registry.npmjs.org/",
15
- "provenance": true
15
+ "provenance": true,
16
+ "access": "public"
16
17
  },
17
18
  "engines": {
18
19
  "node": ">=18.0.0"
@@ -59,23 +60,26 @@
59
60
  "author": "Matthew Spahr",
60
61
  "license": "MIT",
61
62
  "dependencies": {
62
- "axios": "^1.12.1"
63
+ "axios": "^1.12.1",
64
+ "partysocket": "^1.1.10",
65
+ "ws": "^8.18.3"
63
66
  },
64
67
  "devDependencies": {
65
- "@eslint/js": "^9.35.0",
66
- "@jest/globals": "^30.1.2",
67
- "@types/node": "^22.18.3",
68
- "dotenv": "^17.2.2",
69
- "eslint": "^9.35.0",
70
- "eslint-plugin-jest": "^29.0.1",
71
- "jest": "^30.1.3",
72
- "prettier": "^3.6.2",
73
- "ts-jest": "^29.4.1",
74
- "tsup": "^8.5.0",
75
- "tsx": "^4.20.5",
76
- "typedoc": "^0.28.12",
68
+ "@eslint/js": "^9.39.2",
69
+ "@jest/globals": "^30.2.0",
70
+ "@types/node": "^24.10.9",
71
+ "@types/ws": "^8.18.1",
72
+ "dotenv": "^17.2.3",
73
+ "eslint": "^9.39.2",
74
+ "eslint-plugin-jest": "^29.12.1",
75
+ "jest": "^30.2.0",
76
+ "prettier": "^3.8.1",
77
+ "ts-jest": "^29.4.6",
78
+ "tsup": "^8.5.1",
79
+ "tsx": "^4.21.0",
80
+ "typedoc": "^0.28.16",
77
81
  "typedoc-github-theme": "^0.3.1",
78
- "typescript": "^5.8.3",
79
- "typescript-eslint": "^8.43.0"
82
+ "typescript": "^5.9.3",
83
+ "typescript-eslint": "^8.54.0"
80
84
  }
81
85
  }