Skip to content

Commit

Permalink
chore: making some docs
Browse files Browse the repository at this point in the history
  • Loading branch information
wildduck2 committed Jul 30, 2024
1 parent dc59124 commit 6d67d58
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 12 deletions.
5 changes: 3 additions & 2 deletions src/MailboxHeader/__test__/MailboxHeader.test.ts
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, test, vi } from "vitest";
import { beforeEach, describe, expect, test } from "vitest";
import { MIMEMessageHeader } from "../MailboxHeader";
import { Mailbox } from "../../Mailbox/Mailbox";
import { MIMEError } from "../../Error";
Expand All @@ -15,9 +15,10 @@ describe("MIMEMessageHeader", () => {
});

test("should generate a Message-ID header", () => {
// ts-ignore-expect-error
header.fields.find((field) => field.name === "From")!.value = new Mailbox(
"test@example.com"
);
) as any;
const messageId = header.fields.find(
(field) => field.name === "Message-ID"
)!.generator!();
Expand Down
197 changes: 187 additions & 10 deletions src/MimeMessage/MimeMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Base64 } from "../Base64";
import { MIMEError } from "../Error";
import type {
AttachmentOptions,
Boundaries,
ContentOptions,
Email,
MailboxAddrObject,
Expand All @@ -14,25 +15,62 @@ import { MIMEMessageHeader } from "../MailboxHeader";
import { MIMEMessageContent } from "../MimeMessageContent";
import type { MIMEMessageClass } from "./MimeMessage.types";

/**
* Represents a MIME (Multipurpose Internet Mail Extensions) message.
* This class is responsible for constructing and encoding MIME messages, handling headers, content, and attachments.
*/
export class MIMEMessage implements MIMEMessageClass {
headers;
boundaries = { mixed: "", alt: "", related: "" };
validTypes = ["text/html", "text/plain"];
validContentTransferEncodings = [
/**
* The headers of the MIME message.
* @type {MIMEMessageHeader}
*/
headers: MIMEMessageHeader;

/**
* The boundaries used to separate different parts of the MIME message.
* @type {Boundaries}
*/
boundaries: Boundaries = { mixed: "", alt: "", related: "" };

/**
* List of valid MIME content types.
* @type {string[]}
*/
validTypes: string[] = ["text/html", "text/plain"];

/**
* List of valid content transfer encodings.
* @type {string[]}
*/
validContentTransferEncodings: string[] = [
"7bit",
"8bit",
"binary",
"quoted-printable",
"base64",
];

/**
* Array of MIME message contents (text, HTML, attachments).
* @type {MIMEMessageContent[]}
*/
messages: MIMEMessageContent[] = [];

/**
* Constructs a new MIMEMessage instance and initializes boundaries.
*/
constructor() {
this.headers = new MIMEMessageHeader();
this.messages = [];
this.generateBoundaries();
}

asRaw() {
/**
* Generates and returns the raw MIME message as a string.
* @returns {string} The raw MIME message.
* @throws {MIMEError} Throws an error if no content is added to the message.
*/
asRaw(): string {
const eol = "\r\n";
const lines = this.headers.dump();
const plaintext = this.getMessageByType("text/plain");
Expand Down Expand Up @@ -152,10 +190,21 @@ export class MIMEMessage implements MIMEMessageClass {
}
}

/**
* Encodes the MIME message into Base64 format.
* @returns {string} The Base64 encoded MIME message.
*/
asEncoded(): string {
return Base64.toBufferURI(this.asRaw());
}

/**
* Dumps text content as a string formatted with multipart boundaries.
* @param {MIMEMessageContent | undefined} plaintext The plain text message content.
* @param {MIMEMessageContent | undefined} html The HTML message content.
* @param {string} boundary The boundary string for separating parts.
* @returns {string} The formatted text content.
*/
dumpTextContent(
plaintext: MIMEMessageContent | undefined,
html: MIMEMessageContent | undefined,
Expand Down Expand Up @@ -211,24 +260,45 @@ export class MIMEMessage implements MIMEMessageClass {
return data;
}

/**
* Checks if the MIME message contains inline attachments.
* @returns {boolean} True if there are inline attachments, false otherwise.
*/
hasInlineAttachments(): boolean {
return this.messages.some((msg) => msg.isInlineAttachment());
}

/**
* Checks if the MIME message contains any attachments.
* @returns {boolean} True if there are attachments, false otherwise.
*/
hasAttachments(): boolean {
return this.messages.some((msg) => msg.isAttachment());
}

/**
* Retrieves all attachments from the MIME message.
* @returns {MIMEMessageContent[]} Array of MIME message contents that are attachments.
*/
getAttachments(): MIMEMessageContent[] {
const matcher = (msg: MIMEMessageContent) => msg.isAttachment();
return this.messages.some(matcher) ? this.messages.filter(matcher) : [];
}

/**
* Retrieves all inline attachments from the MIME message.
* @returns {MIMEMessageContent[]} Array of MIME message contents that are inline attachments.
*/
getInlineAttachments(): MIMEMessageContent[] {
const matcher = (msg: MIMEMessageContent) => msg.isInlineAttachment();
return this.messages.some(matcher) ? this.messages.filter(matcher) : [];
}

/**
* Retrieves the message content of a specific MIME type (text/plain or text/html).
* @param {string} type The MIME type to filter by (e.g., "text/plain" or "text/html").
* @returns {MIMEMessageContent | undefined} The MIME message content of the specified type, or undefined if not found.
*/
getMessageByType(type: string): MIMEMessageContent | undefined {
const matcher = (msg: MIMEMessageContent) =>
!msg.isAttachment() &&
Expand All @@ -239,6 +309,12 @@ export class MIMEMessage implements MIMEMessageClass {
: undefined;
}

/**
* Adds an attachment to the MIME message.
* @param {AttachmentOptions} opts The options for the attachment, including data, filename, and headers.
* @returns {MIMEMessageContent} The MIME message content representing the attachment.
* @throws {MIMEError} Throws an error if the filename is missing or if the content type is invalid.
*/
addAttachment(opts: AttachmentOptions): MIMEMessageContent {
if (!this.isObject(opts.headers)) opts.headers = {};
if (typeof opts.filename !== "string") {
Expand Down Expand Up @@ -277,6 +353,12 @@ export class MIMEMessage implements MIMEMessageClass {
return this._addMessage({ data: opts.data, headers: opts.headers });
}

/**
* Adds a message (text or HTML content) to the MIME message.
* @param {ContentOptions} opts The options for the content, including data, type, and headers.
* @returns {MIMEMessageContent} The MIME message content representing the added message.
* @throws {MIMEError} Throws an error if the content type is invalid.
*/
addMessage(opts: ContentOptions): MIMEMessageContent {
if (!this.isObject(opts.headers)) opts.headers = {};
let type = opts.headers["Content-Type"] || opts.contentType || "none";
Expand All @@ -301,12 +383,24 @@ export class MIMEMessage implements MIMEMessageClass {
return this._addMessage({ data: opts.data, headers: opts.headers });
}

/**
* Internal method to add a MIME message content to the array of messages.
* @param {any} opts The options for the message, including data and headers.
* @returns {MIMEMessageContent} The MIME message content added.
*/
_addMessage(opts: any): MIMEMessageContent {
const msg = new MIMEMessageContent(opts.data, opts.headers);
this.messages.push(msg);
return msg;
}

/**
* Sets the sender of the MIME message.
* @param {MailboxAddrObject | MailboxAddrText | Email} input The sender's address.
* @param {Object} config Configuration object.
* @param {MailboxType} config.type The type of mailbox (e.g., "From").
* @returns {MailboxClass} The mailbox instance representing the sender.
*/
setSender(
input: MailboxAddrObject | MailboxAddrText | Email,
config: { type: MailboxType } = { type: "From" }
Expand All @@ -316,10 +410,21 @@ export class MIMEMessage implements MIMEMessageClass {
return mailbox as MailboxClass;
}

/**
* Retrieves the sender of the MIME message.
* @returns {MailboxClass | undefined | string} The sender's mailbox instance, or undefined if not set.
*/
getSender(): MailboxClass | undefined | string {
return this.getHeader("From");
}

/**
* Sets the recipients of the MIME message.
* @param {MailboxAddrObject | MailboxAddrText | Email} input The recipient's address.
* @param {Object} config Configuration object.
* @param {MailboxType} config.type The type of mailbox (e.g., "To", "Cc").
* @returns {MailboxClass[]} Array of mailbox instances representing the recipients.
*/
setRecipients(
input: MailboxAddrObject | MailboxAddrText | Email,
config: { type: MailboxType } = { type: "To" }
Expand All @@ -330,73 +435,145 @@ export class MIMEMessage implements MIMEMessageClass {
return recs as MailboxClass[];
}

/**
* Retrieves the recipients of the MIME message.
* @param {Object} config Configuration object.
* @param {MailboxType} config.type The type of mailbox (e.g., "To").
* @returns {MailboxClass | MailboxClass[] | undefined} The recipient(s) as mailbox instances, or undefined if not set.
*/
getRecipients(
config = { type: "To" }
config: { type: MailboxType } = { type: "To" }
): MailboxClass | MailboxClass[] | undefined {
return this.getHeader(config.type) as unknown as
| MailboxClass
| MailboxClass[]
| undefined;
}

/**
* Sets a single recipient (usually for "To" field).
* @param {string} input The recipient's address.
* @returns {MailboxClass[]} Array of mailbox instances representing the recipient.
*/
setRecipient(input: string): MailboxClass[] {
return this.setRecipients(input, { type: "To" });
}

/**
* Sets the "To" recipients of the MIME message.
* @param {string} input The recipient's address.
* @returns {MailboxClass[]} Array of mailbox instances representing the recipients.
*/
setTo(input: string): MailboxClass[] {
return this.setRecipients(input, { type: "To" });
}

/**
* Sets the "Cc" recipients of the MIME message.
* @param {string} input The recipient's address.
* @returns {MailboxClass[]} Array of mailbox instances representing the recipients.
*/
setCc(input: string): MailboxClass[] {
return this.setRecipients(input, { type: "Cc" });
}

/**
* Sets the "Reply-To" recipients of the MIME message.
* @param {string} input The recipient's address.
* @returns {MailboxClass[]} Array of mailbox instances representing the recipients.
*/
setReplyTo(input: string): MailboxClass[] {
return this.setRecipients(input, { type: "Reply-To" });
}

/**
* Sets the "Bcc" recipients of the MIME message.
* @param {string} input The recipient's address.
* @returns {MailboxClass[]} Array of mailbox instances representing the recipients.
*/
setBcc(input: string): MailboxClass[] {
return this.setRecipients(input, { type: "Bcc" });
}

/**
* Retrieves the "To" recipients of the MIME message.
* @returns {MailboxClass[] | undefined} Array of mailbox instances representing the "To" recipients, or undefined if not set.
*/
setSubject(value: string): string {
this.setHeader("subject", value);
return value;
}

/**
* Retrieves the "Cc" recipients of the MIME message.
* @returns {MailboxClass[] | undefined} Array of mailbox instances representing the "Cc" recipients, or undefined if not set.
*/
getSubject(): string {
return this.getHeader("subject") as string;
}

setHeader(name: string, value: unknown) {
/**
* Sets a header for the MIME message.
* @param {string} name The name of the header.
* @param {unknown} value The value of the header.
* @returns {string} The name of the header that was set.
*/
setHeader(name: string, value: unknown): string {
this.headers.set(name, value);
return name;
}

/**
* Retrieves a header value from the MIME message.
* @param {string} name The name of the header to retrieve.
* @returns {string} The value of the header.
*/
getHeader(name: string): string {
return this.headers.get(name) as string;
}

setHeaders(obj: Record<string, unknown>) {
/**
* Sets multiple headers for the MIME message.
* @param {Record<string, unknown>} obj An object containing header names and values.
* @returns {string[]} An array of header names that were set.
*/
setHeaders(obj: Record<string, unknown>): string[] {
return Object.keys(obj).map((prop) => this.setHeader(prop, obj[prop]));
}

getHeaders() {
/**
* Retrieves all headers of the MIME message as an object.
* @returns {Record<string, string>} An object containing all headers.
*/
getHeaders(): Record<string, string> {
return this.headers.toObject();
}

generateBoundaries() {
/**
* Generates unique boundary strings for different parts of the MIME message.
*/
generateBoundaries(): void {
this.boundaries = {
mixed: Math.random().toString(36).slice(2),
alt: Math.random().toString(36).slice(2),
related: Math.random().toString(36).slice(2),
};
}

/**
* Checks if a value is an array.
* @param {unknown} v The value to check.
* @returns {boolean} True if the value is an array, false otherwise.
*/
isArray(v: unknown): v is Array<unknown> {
return !!v && v.constructor === Array;
}

/**
* Checks if a value is an object.
* @param {unknown} v The value to check.
* @returns {boolean} True if the value is an object, false otherwise.
*/
isObject(v: unknown): v is Object {
return !!v && v.constructor === Object;
}
Expand Down

0 comments on commit 6d67d58

Please sign in to comment.