import "./errors-CzM80k9O.js";
import { t as debug_default } from "./debug-BT60qQj9.js";
import { t as MemoryQueueMessenger } from "./memory_queue-CRXpF8-D.js";
import { t as stubsRoot } from "./main-DbL06a4f.js";
import "./mail_response-BVfBKOpz.js";
import { t as JSONTransport } from "./json-dosS83v3.js";
import { RuntimeException } from "@adonisjs/core/exceptions";
import { basename } from "node:path";
import { fileURLToPath } from "node:url";
import Macroable from "@poppinss/macroable";
import { AssertionError } from "node:assert";
import string from "@adonisjs/core/helpers/string";
import ical from "ical-generator";
import { configProvider } from "@adonisjs/core";
//#region src/message.ts
/**
* Fluent API to construct node mailer message object
*/
var Message = class Message extends Macroable {
	/**
	* The template engine for rendering templates to compute
	* the message contents
	*/
	static templateEngine;
	/**
	* Use the configured template engine to compute
	* the email contents from templates
	*/
	static async computeContentsFor({ message, views, sharedState }) {
		const getTemplateEngine = () => {
			if (!this.templateEngine) throw new RuntimeException("Cannot render email templates without a template engine");
			return this.templateEngine;
		};
		/**
		* Helpers to be shared with templates
		*/
		const viewHelpers = {
			embedImage: (filePath, options) => {
				const cid = string.uuid();
				message.attachments = message.attachments ?? [];
				message.attachments.push({
					path: filePath,
					cid,
					filename: basename(filePath),
					...options
				});
				return `cid:${cid}`;
			},
			embedImageData: (data, options) => {
				const cid = string.uuid();
				message.attachments = message.attachments || [];
				message.attachments.push({
					content: data,
					cid,
					...options
				});
				return `cid:${cid}`;
			}
		};
		if (!message.html && views.html) {
			debug_default("computing mail html contents %O", views.html);
			message.html = await getTemplateEngine().render(views.html.template, {
				...sharedState,
				...viewHelpers
			}, views.html.data);
		}
		/**
		* Do not share view helpers with text image, since they cannot
		* render images
		*/
		if (!message.text && views.text) {
			debug_default("computing mail text contents %O", views.text);
			message.text = await getTemplateEngine().render(views.text.template, { ...sharedState }, views.text.data);
		}
		if (!message.watch && views.watch) {
			debug_default("computing mail watch contents %O", views.watch);
			message.watch = await getTemplateEngine().render(views.watch.template, {
				...sharedState,
				...viewHelpers
			}, views.watch.data);
		}
	}
	/**
	* Nodemailer internally mutates the "attachments" object
	* and removes the path property with the contents of
	* the file.
	*
	* Therefore, we need an additional attachment array we can
	* use to searching attachments and writing assertions
	*/
	#attachmentsForSearch = [];
	/**
	* Templates to use for rendering email body for
	* HTML, plain text and watch
	*/
	contentViews = {};
	/**
	* Reference to the underlying node mailer message
	*/
	nodeMailerMessage = {};
	/**
	* Converts a recipient email and name to formatted
	* string
	*/
	formatRecipient(recipient) {
		if (!recipient) return;
		if (typeof recipient === "string") return recipient;
		if (!recipient.name) return recipient.address;
		return `${recipient.name} <${recipient.address}>`;
	}
	/**
	* Check if a given recipient exists for the mentioned
	* email and name.
	*/
	hasRecipient(property, address, name) {
		const recipients = this.nodeMailerMessage[property];
		if (!recipients) return false;
		/**
		* When checking for name and email both
		*/
		if (name) return !!recipients.find((recipient) => {
			if (typeof recipient === "string") return false;
			return recipient.address === address && recipient.name === name;
		});
		/**
		* When checking for just the email
		*/
		return !!recipients.find((recipient) => {
			if (typeof recipient === "string") return recipient === address;
			return recipient.address === address;
		});
	}
	/**
	* Assert the message is sent to the mentioned address
	*/
	assertRecipient(property, address, name) {
		if (!this.hasRecipient(property, address, name)) {
			const expected = this.formatRecipient({
				address,
				name: name || ""
			});
			const actual = this.nodeMailerMessage[property]?.map((recipient) => {
				return this.formatRecipient(recipient);
			}) || [];
			throw new AssertionError({
				message: `Expected message to be delivered to "${expected}"`,
				expected: [expected],
				actual,
				operator: "includes"
			});
		}
	}
	/**
	* Add recipient as `to`
	*/
	to(address, name) {
		this.nodeMailerMessage.to = this.nodeMailerMessage.to || [];
		this.nodeMailerMessage.to.push(name ? {
			address,
			name
		} : address);
		return this;
	}
	/**
	* Check if message is sent to the mentioned address
	*/
	hasTo(address, name) {
		return this.hasRecipient("to", address, name);
	}
	/**
	* Assert the message is sent to the mentioned address
	*/
	assertTo(address, name) {
		return this.assertRecipient("to", address, name);
	}
	/**
	* Add `from` name and email
	*/
	from(address, name) {
		this.nodeMailerMessage.from = name ? {
			address,
			name
		} : address;
		return this;
	}
	/**
	* Check if message is sent from the mentioned address
	*/
	hasFrom(address, name) {
		const fromAddress = this.nodeMailerMessage.from;
		if (!fromAddress) return false;
		/**
		* When checking for name and email both
		*/
		if (name) {
			if (typeof fromAddress === "string") return false;
			return fromAddress.address === address && fromAddress.name === name;
		}
		/**
		* When checking for just the email
		*/
		if (typeof fromAddress === "string") return fromAddress === address;
		return fromAddress.address === address;
	}
	/**
	* Assert the message is sent from the mentioned address
	*/
	assertFrom(address, name) {
		if (!this.hasFrom(address, name)) {
			const expected = this.formatRecipient({
				address,
				name: name || ""
			});
			const actual = this.formatRecipient(this.nodeMailerMessage.from);
			throw new AssertionError({
				message: `Expected message to be sent from "${expected}"`,
				expected,
				actual
			});
		}
	}
	cc(addresses, name) {
		this.nodeMailerMessage.cc = this.nodeMailerMessage.cc || [];
		if (typeof addresses === "string") this.nodeMailerMessage.cc.push(name ? {
			address: addresses,
			name
		} : addresses);
		else addresses.forEach((address) => {
			this.nodeMailerMessage.cc.push(address);
		});
		return this;
	}
	/**
	* Check if message is sent to the mentioned address
	*/
	hasCc(address, name) {
		return this.hasRecipient("cc", address, name);
	}
	/**
	* Assert the message is sent to the mentioned address
	*/
	assertCc(address, name) {
		return this.assertRecipient("cc", address, name);
	}
	bcc(addresses, name) {
		this.nodeMailerMessage.bcc = this.nodeMailerMessage.bcc || [];
		if (typeof addresses === "string") this.nodeMailerMessage.bcc.push(name ? {
			address: addresses,
			name
		} : addresses);
		else addresses.forEach((address) => {
			this.nodeMailerMessage.bcc.push(address);
		});
		return this;
	}
	/**
	* Check if message is sent to the mentioned address
	*/
	hasBcc(address, name) {
		return this.hasRecipient("bcc", address, name);
	}
	/**
	* Assert the message is sent to the mentioned address
	*/
	assertBcc(address, name) {
		return this.assertRecipient("bcc", address, name);
	}
	/**
	* Define custom message id
	*/
	messageId(messageId) {
		this.nodeMailerMessage.messageId = messageId;
		return this;
	}
	/**
	* Define subject
	*/
	subject(message) {
		this.nodeMailerMessage.subject = message;
		return this;
	}
	/**
	* Check if the message has the mentioned subject
	*/
	hasSubject(message) {
		return !!this.nodeMailerMessage.subject && this.nodeMailerMessage.subject === message;
	}
	/**
	* Assert the message has the mentioned subject
	*/
	assertSubject(message) {
		if (!this.hasSubject(message)) throw new AssertionError({
			message: `Expected message subject to be "${message}"`,
			expected: message,
			actual: this.nodeMailerMessage.subject
		});
	}
	/**
	* Define replyTo email and name
	*/
	replyTo(address, name) {
		this.nodeMailerMessage.replyTo = this.nodeMailerMessage.replyTo || [];
		this.nodeMailerMessage.replyTo.push(name ? {
			address,
			name
		} : address);
		return this;
	}
	/**
	* Check if the mail has the mentioned reply to address
	*/
	hasReplyTo(address, name) {
		return this.hasRecipient("replyTo", address, name);
	}
	/**
	* Assert the mail has the mentioned reply to address
	*/
	assertReplyTo(address, name) {
		if (!this.hasRecipient("replyTo", address, name)) {
			const expected = this.formatRecipient({
				address,
				name: name || ""
			});
			const actual = this.nodeMailerMessage.replyTo?.map((recipient) => {
				return this.formatRecipient(recipient);
			}) || [];
			throw new AssertionError({
				message: `Expected reply-to addresses to include "${expected}"`,
				expected: [expected],
				actual,
				operator: "includes"
			});
		}
	}
	/**
	* Define inReplyTo message id
	*/
	inReplyTo(messageId) {
		this.nodeMailerMessage.inReplyTo = messageId;
		return this;
	}
	/**
	* Define multiple message id's as references
	*/
	references(messagesIds) {
		this.nodeMailerMessage.references = messagesIds;
		return this;
	}
	/**
	* Optionally define email envelope
	*/
	envelope(envelope) {
		this.nodeMailerMessage.envelope = envelope;
		return this;
	}
	/**
	* Define contents encoding
	*/
	encoding(encoding) {
		this.nodeMailerMessage.encoding = encoding;
		return this;
	}
	/**
	* Define email priority
	*/
	priority(priority) {
		this.nodeMailerMessage.priority = priority;
		return this;
	}
	/**
	* Compute email html from defined view
	*/
	htmlView(template, data) {
		this.contentViews.html = {
			template,
			data
		};
		return this;
	}
	/**
	* Compute email text from defined view
	*/
	textView(template, data) {
		this.contentViews.text = {
			template,
			data
		};
		return this;
	}
	/**
	* Compute apple watch html from defined view
	*/
	watchView(template, data) {
		this.contentViews.watch = {
			template,
			data
		};
		return this;
	}
	/**
	* Compute email html from raw text
	*/
	html(content) {
		this.nodeMailerMessage.html = content;
		return this;
	}
	/**
	* Compute email text from raw text
	*/
	text(content) {
		this.nodeMailerMessage.text = content;
		return this;
	}
	/**
	* Compute email watch html from raw text
	*/
	watch(content) {
		this.nodeMailerMessage.watch = content;
		return this;
	}
	/**
	* Assert content of mail to include substring or match
	* a given regular expression
	*/
	assertContent(property, substring) {
		const contents = this.nodeMailerMessage[property];
		if (!contents) throw new AssertionError({ message: `Expected message ${property} body to match substring, but it is undefined` });
		if (typeof substring === "string") {
			if (!String(contents).includes(substring)) throw new AssertionError({ message: `Expected message ${property} body to include "${substring}"` });
			return;
		}
		if (!substring.test(String(contents))) throw new AssertionError({ message: `Expected message ${property} body to match "${substring}"` });
	}
	/**
	* Assert message plain text contents to include
	* substring or match the given regular expression
	*/
	assertTextIncludes(substring) {
		return this.assertContent("text", substring);
	}
	/**
	* Assert message HTML contents to include substring
	* or match the given regular expression
	*/
	assertHtmlIncludes(substring) {
		return this.assertContent("html", substring);
	}
	/**
	* Assert message watch contents to include substring
	* or match the given regular expression
	*/
	assertWatchIncludes(substring) {
		return this.assertContent("watch", substring);
	}
	/**
	* Define one or attachments
	*/
	attach(file, options) {
		const filePath = typeof file === "string" ? file : fileURLToPath(file);
		this.nodeMailerMessage.attachments = this.nodeMailerMessage.attachments || [];
		this.nodeMailerMessage.attachments.push({
			path: filePath,
			filename: basename(filePath),
			...options
		});
		this.#attachmentsForSearch.push({
			path: filePath,
			filename: basename(filePath),
			...options
		});
		return this;
	}
	hasAttachment(file, options) {
		const attachments = this.#attachmentsForSearch;
		if (typeof file === "function") return !!attachments.find(file);
		const filePath = typeof file === "string" ? file : fileURLToPath(file);
		return !!attachments.find((attachment) => {
			const hasMatchingPath = attachment.path ? String(attachment.path).endsWith(filePath) : false;
			if (!options) return hasMatchingPath;
			if (options.filename && attachment.filename !== options.filename) return false;
			if (options.cid && attachment.cid !== options.cid) return false;
			return true;
		});
	}
	assertAttachment(file, options) {
		if (typeof file === "function") {
			if (!this.hasAttachment(file)) throw new AssertionError({ message: `Expected assertion callback to find an attachment` });
			return;
		}
		if (!this.hasAttachment(file, options)) throw new AssertionError({
			message: `Expected message attachments to include "${file}"`,
			expected: [{
				path: file,
				...options
			}],
			actual: this.nodeMailerMessage.attachments,
			operator: "includes"
		});
	}
	/**
	* Define attachment from raw data
	*/
	attachData(content, options) {
		this.nodeMailerMessage.attachments = this.nodeMailerMessage.attachments || [];
		this.nodeMailerMessage.attachments.push({
			content,
			...options
		});
		return this;
	}
	/**
	* Embed attachment inside content using `cid`
	*/
	embed(file, cid, options) {
		const filePath = typeof file === "string" ? file : fileURLToPath(file);
		this.nodeMailerMessage.attachments = this.nodeMailerMessage.attachments || [];
		this.nodeMailerMessage.attachments.push({
			path: filePath,
			cid,
			filename: basename(filePath),
			...options
		});
		this.#attachmentsForSearch.push({
			path: filePath,
			cid,
			filename: basename(filePath),
			...options
		});
		return this;
	}
	/**
	* Embed attachment from raw data inside content using `cid`
	*/
	embedData(content, cid, options) {
		this.nodeMailerMessage.attachments = this.nodeMailerMessage.attachments || [];
		this.nodeMailerMessage.attachments.push({
			content,
			cid,
			...options
		});
		return this;
	}
	/**
	* Define custom headers for email
	*/
	header(key, value) {
		if (!this.nodeMailerMessage.headers) this.nodeMailerMessage.headers = {};
		if (!Array.isArray(this.nodeMailerMessage.headers)) this.nodeMailerMessage.headers[key] = value;
		return this;
	}
	/**
	* Check if a header has been defined and optionally
	* check for values as well.
	*/
	hasHeader(key, value) {
		const headers = this.nodeMailerMessage.headers;
		if (!headers || Array.isArray(headers)) return false;
		const headerValue = headers[key];
		if (!headerValue) return false;
		if (value) return !!(Array.isArray(value) ? value : [value]).every((one) => {
			return typeof headerValue === "string" ? headerValue === one : Array.isArray(headerValue) ? headerValue.includes(one) : headerValue.value === one;
		});
		return true;
	}
	/**
	* Assert a header has been defined and optionally
	* check for values as well.
	*/
	assertHeader(key, value) {
		if (!this.hasHeader(key, value)) {
			const headers = this.nodeMailerMessage.headers;
			const actual = headers && !Array.isArray(headers) ? headers[key] : void 0;
			if (!value || !actual) throw new AssertionError({ message: `Expected message headers to include "${key}"` });
			throw new AssertionError({
				message: `Expected message headers to include "${key}" with value "${value}"`,
				actual,
				expected: value
			});
		}
	}
	/**
	* Define custom prepared headers for email
	*/
	preparedHeader(key, value) {
		if (!this.nodeMailerMessage.headers) this.nodeMailerMessage.headers = {};
		if (!Array.isArray(this.nodeMailerMessage.headers)) this.nodeMailerMessage.headers[key] = {
			prepared: true,
			value
		};
		return this;
	}
	/**
	* Defines a `List-` prefix header on the email. Calling
	* this method multiple times for the same key will
	* override the old value.
	*/
	addListHeader(key, value) {
		this.nodeMailerMessage.list = this.nodeMailerMessage.list || {};
		this.nodeMailerMessage.list[key] = value;
		return this;
	}
	/**
	* Add `List-Help` header. Calling this method multiple
	* times will override the existing value
	*/
	listHelp(value) {
		return this.addListHeader("help", value);
	}
	/**
	* Add `List-Unsubscribe` header. Calling this method multiple
	* times will override the existing value.
	*
	* Pass `{ oneClick: true }` to also emit the RFC 8058
	* `List-Unsubscribe-Post: List-Unsubscribe=One-Click` header, which
	* signals to mailbox providers (Gmail, Yahoo, etc.) that the
	* unsubscribe URL accepts a one-click HTTP POST. The URL passed as
	* `value` must be an `https` endpoint that handles the POST.
	*/
	listUnsubscribe(value, options) {
		this.addListHeader("unsubscribe", value);
		/**
		* `List-Unsubscribe-Post` carries a fixed RFC 8058 token, not a URL.
		* It must be sent verbatim, so it goes through the prepared header
		* path instead of `addListHeader`, which would URL-encode the value
		* into `<http://List-Unsubscribe=One-Click>`.
		*/
		if (options?.oneClick) this.preparedHeader("List-Unsubscribe-Post", "List-Unsubscribe=One-Click");
		return this;
	}
	/**
	* Add `List-Subscribe` header. Calling this method multiple
	* times will override the existing value
	*/
	listSubscribe(value) {
		return this.addListHeader("subscribe", value);
	}
	/**
	* Attach a calendar event and define contents as string
	*/
	icalEvent(contents, options) {
		if (typeof contents === "function") {
			const calendar = ical();
			contents(calendar);
			contents = calendar.toString();
		}
		this.nodeMailerMessage.icalEvent = {
			content: contents,
			...options
		};
		return this;
	}
	/**
	* Attach a calendar event and load contents from a file
	*/
	icalEventFromFile(file, options) {
		const filePath = typeof file === "string" ? file : fileURLToPath(file);
		this.nodeMailerMessage.icalEvent = {
			path: filePath,
			...options
		};
		return this;
	}
	/**
	* Attach a calendar event and load contents from a url
	*/
	icalEventFromUrl(url, options) {
		this.nodeMailerMessage.icalEvent = {
			href: url,
			...options
		};
		return this;
	}
	/**
	* Computes email contents by rendering the configured
	* templates
	*/
	async computeContents(sharedState) {
		await Message.computeContentsFor({
			message: this.nodeMailerMessage,
			views: this.contentViews,
			sharedState: sharedState ?? {}
		});
	}
	/**
	* Object representation of the message
	*/
	toObject() {
		return {
			message: this.nodeMailerMessage,
			views: { ...this.contentViews }
		};
	}
	/**
	* JSON representation of the message
	*/
	toJSON() {
		return this.toObject();
	}
};
//#endregion
//#region src/base_mail.ts
/**
* Class based emails are self contained dispatchable
* mail objects
*/
var BaseMail = class {
	/**
	* A flag to track if the message has been built
	*/
	built = false;
	/**
	* A flag to track if the mail templates have been rendered
	*/
	renderedTemplates = false;
	/**
	* Reference to the mail message object
	*/
	message = new Message();
	/**
	* Define the email subject
	*/
	subject;
	/**
	* Define the from address for the email
	*/
	from;
	/**
	* Define the replyTo email address
	*/
	replyTo;
	/**
	* Defines the subject on the message using the mail
	* class subject property
	*/
	defineSubject() {
		if (this.subject) this.message.subject(this.subject);
	}
	/**
	* Defines the from on the message using the mail
	* class from property
	*/
	defineSender() {
		if (this.from) typeof this.from === "string" ? this.message.from(this.from) : this.message.from(this.from.address, this.from.name);
		if (this.replyTo) typeof this.replyTo === "string" ? this.message.replyTo(this.replyTo) : this.message.replyTo(this.replyTo.address, this.replyTo.name);
	}
	/**
	* Builds the mail message for sending it
	*/
	async build() {
		if (this.built) return;
		this.built = true;
		this.defineSubject();
		this.defineSender();
		await this.prepare();
	}
	/**
	* Builds the mail message with the email contents.
	* This method will render the templates ahead of
	* time
	*/
	async buildWithContents() {
		await this.build();
		if (this.renderedTemplates) return;
		await this.message.computeContents();
	}
	/**
	* Sends the mail
	*/
	async send(mailer, config) {
		await this.build();
		return mailer.sendCompiled(this.message.toObject(), config);
	}
	/**
	* Sends the mail by using the background
	* messenger
	*/
	async sendLater(mailer, config) {
		await this.build();
		return mailer.sendLaterCompiled(this.message.toObject(), config);
	}
};
//#endregion
//#region src/mailer.ts
/**
* The Mailer acts as an adapter that wraps a transport and exposes
* consistent API for sending and queueing emails
*/
var Mailer = class {
	name;
	transport;
	config;
	/**
	* Reference to AdonisJS application emitter
	*/
	#emitter;
	/**
	* Messenger to use for queuing emails
	*/
	#messenger;
	constructor(name, transport, emitter, config = {}) {
		this.name = name;
		this.transport = transport;
		this.config = config;
		this.#emitter = emitter;
		this.#messenger = new MemoryQueueMessenger(this, this.#emitter);
	}
	/**
	* Configure the messenger to use for sending email asynchronously
	*/
	setMessenger(messenger) {
		this.#messenger = messenger;
		return this;
	}
	/**
	* Sends a compiled email using the underlying transport
	*/
	async sendCompiled(mail, sendConfig) {
		/**
		* Use the global from address when no from address
		* is defined on the mail
		*/
		if (!mail.message.from && this.config.from) mail.message.from = this.config.from;
		/**
		* Use the global from address when no from address
		* is defined on the mail
		*/
		if (!mail.message.replyTo && this.config.replyTo) mail.message.replyTo = [this.config.replyTo];
		/**
		* Notify, about to send the email
		*/
		this.#emitter.emit("mail:sending", {
			...mail,
			mailerName: this.name
		});
		/**
		* Mutates the "compiledMessage.message" object based upon
		* the configured templates
		*/
		await Message.computeContentsFor({
			...mail,
			sharedState: this.config.globals ?? {}
		});
		/**
		* Send the message using the transport
		*/
		debug_default("sending email, subject \"%s\"", mail.message.subject);
		const response = await this.transport.send(mail.message, sendConfig);
		debug_default("email sent, message id \"%s\"", response.messageId);
		/**
		* Notify, email has been sent
		*/
		this.#emitter.emit("mail:sent", {
			...mail,
			mailerName: this.name,
			response
		});
		return response;
	}
	/**
	* Queues a compiled email
	*/
	async sendLaterCompiled(compiledMessage, sendConfig) {
		/**
		* Notify, we are queueing the email
		*/
		this.#emitter.emit("mail:queueing", {
			...compiledMessage,
			mailerName: this.name
		});
		/**
		* Queuing email
		*/
		debug_default("queueing email");
		const metaData = await this.#messenger.queue(compiledMessage, sendConfig);
		/**
		* Notify, the email has been queued
		*/
		this.#emitter.emit("mail:queued", {
			...compiledMessage,
			metaData,
			mailerName: this.name
		});
	}
	/**
	* Sends email
	*/
	async send(callbackOrMail, config) {
		if (callbackOrMail instanceof BaseMail) return callbackOrMail.send(this, config);
		const message = new Message();
		/**
		* Set the default from address, the user can override it
		* inside the callback
		*/
		if (this.config.from) typeof this.config.from === "string" ? message.from(this.config.from) : message.from(this.config.from.address, this.config.from.name);
		/**
		* Invoke callback to configure the mail message
		*/
		await callbackOrMail(message);
		/**
		* Compile the message to an object
		*/
		const compiledMessage = message.toObject();
		return this.sendCompiled(compiledMessage, config);
	}
	/**
	* Send an email asynchronously using the mail messenger. The
	* default messenger uses an in-memory queue, unless you have
	* configured a custom messenger.
	*/
	async sendLater(callbackOrMail, config) {
		if (callbackOrMail instanceof BaseMail) return callbackOrMail.sendLater(this, config);
		const message = new Message();
		/**
		* Invoke callback to configure the mail message
		*/
		await callbackOrMail(message);
		/**
		* Compile the message to an object
		*/
		const compiledMessage = message.toObject();
		return this.sendLaterCompiled(compiledMessage, config);
	}
	/**
	* Invokes `close` method on the transport
	*/
	async close() {
		await this.transport.close?.();
	}
};
//#endregion
//#region configure.ts
/**
* List of env variables used by different transports
*/
const ENV_VARIABLES = {
	smtp: {
		SMTP_HOST: "localhost",
		SMTP_PORT: 1025
	},
	ses: {
		AWS_ACCESS_KEY_ID: "your-access-key-id",
		AWS_SECRET_ACCESS_KEY: "your-secret-key",
		AWS_REGION: "us-east-1"
	},
	mailgun: {
		MAILGUN_API_KEY: "your-mailgun-api-key",
		MAILGUN_DOMAIN: "mg.example.com"
	},
	sparkpost: { SPARKPOST_API_KEY: "your-sparkpost-api-key" },
	resend: { RESEND_API_KEY: "your-resend-api-key" },
	brevo: { BREVO_API_KEY: "your-brevo-api-key" },
	postmark: { POSTMARK_API_KEY: "your-postmark-api-key" }
};
/**
* List of supported transports
*/
const KNOWN_TRANSPORTS = Object.keys(ENV_VARIABLES);
/**
* Configures the package
*/
async function configure(command) {
	/**
	* Read transports from the "--transports" CLI flag
	*/
	let selectedTransports = command.parsedFlags.transports;
	/**
	* Display prompts when transports have been selected
	* via the CLI flag
	*/
	if (!selectedTransports) selectedTransports = await command.prompt.multiple("Select the mail services you want to use", KNOWN_TRANSPORTS, { validate(values) {
		return !values || !values.length ? "Please select one or more transports" : true;
	} });
	/**
	* Normalized list of transports
	*/
	const transports = typeof selectedTransports === "string" ? [selectedTransports] : selectedTransports;
	const unknownTransport = transports.find((transport) => !KNOWN_TRANSPORTS.includes(transport));
	if (unknownTransport) {
		command.exitCode = 1;
		command.logger.logError(`Invalid transport "${unknownTransport}". Supported transports are: ${string.sentence(KNOWN_TRANSPORTS)}`);
		return;
	}
	const codemods = await command.createCodemods();
	/**
	* Publish config file
	*/
	await codemods.makeUsingStub(stubsRoot, "config/mail.stub", { transports });
	/**
	* Publish provider and command
	*/
	await codemods.updateRcFile((rcFile) => {
		rcFile.addProvider("@adonisjs/mail/mail_provider");
		rcFile.addCommand("@adonisjs/mail/commands");
	});
	/**
	* Define env variables for the selected transports
	*/
	await codemods.defineEnvVariables(transports.reduce((result, transport) => {
		Object.assign(result, ENV_VARIABLES[transport]);
		return result;
	}, {
		MAIL_MAILER: transports[0],
		MAIL_FROM_NAME: "Your name",
		MAIL_FROM_ADDRESS: "app@yourdomain.com"
	}));
	/**
	* Define env variables validation for the selected transports
	*/
	await codemods.defineEnvValidations({
		leadingComment: "Variables for configuring the mail package",
		variables: transports.reduce((result, transport) => {
			for (const envVariable of Object.keys(ENV_VARIABLES[transport])) result[envVariable] = `Env.schema.${envVariable === "SMTP_PORT" ? "number()" : "string()"}`;
			return result;
		}, {
			MAIL_MAILER: `Env.schema.enum(['${transports.join("','")}'] as const)`,
			MAIL_FROM_NAME: "Env.schema.string()",
			MAIL_FROM_ADDRESS: "Env.schema.string()"
		})
	});
}
//#endregion
//#region src/fake_mailer.ts
/**
* Mails collection to collect mails sent or queued during
* the fake mode
*/
var MailsCollection = class {
	#sent = [];
	#queued = [];
	trackSent(mail) {
		this.#sent.push(mail);
	}
	trackQueued(mail) {
		this.#queued.push(mail);
	}
	clear() {
		this.#sent = [];
		this.#queued = [];
	}
	/**
	* Returns a list of sent emails captured by the fake mailer
	*/
	sent(filterFn) {
		return filterFn ? this.#sent.filter(filterFn) : this.#sent;
	}
	/**
	* Returns a list of queued emails captured by the fake mailer
	*/
	queued(filterFn) {
		return filterFn ? this.#queued.filter(filterFn) : this.#queued;
	}
	/**
	* Assert the mentioned mail was sent during the fake
	* mode
	*/
	assertSent(mailConstructor, findFn) {
		if (!this.#sent.find((mail) => {
			if (!findFn) return mail instanceof mailConstructor;
			return mail instanceof mailConstructor && findFn(mail);
		})) throw new AssertionError({ message: `Expected mail "${mailConstructor.name}" was not sent` });
		return this;
	}
	/**
	* Assert the mentioned mail was NOT sent during the fake
	* mode
	*/
	assertNotSent(mailConstructor, findFn) {
		if (this.#sent.find((mail) => {
			if (!findFn) return mail instanceof mailConstructor;
			return mail instanceof mailConstructor && findFn(mail);
		})) throw new AssertionError({ message: `Unexpected mail "${mailConstructor.name}" was sent` });
		return this;
	}
	assertSentCount(mailConstructor, count) {
		if (typeof mailConstructor === "number") {
			const actual = this.#sent.length;
			const expected = mailConstructor;
			if (actual !== expected) throw new AssertionError({
				message: `Expected to send "${expected}" ${string.pluralize("mail", expected)}, instead received "${actual}" ${string.pluralize("mail", actual)}`,
				actual,
				expected
			});
			return this;
		}
		const actual = this.sent((mail) => mail instanceof mailConstructor).length;
		const expected = count;
		if (actual !== expected) throw new AssertionError({
			message: `Expected "${mailConstructor.name}" to be sent "${expected}" ${string.pluralize("time", expected)}, instead it was sent "${actual}" ${string.pluralize("time", actual)}`,
			actual,
			expected
		});
		return this;
	}
	/**
	* Assert zero emails were sent
	*/
	assertNoneSent() {
		if (this.#sent.length) throw new AssertionError({
			message: `Expected zero mail to be sent, instead received "${this.#sent.length}" mail`,
			expected: [],
			actual: [this.#sent.map((mail) => mail.constructor.name)]
		});
		return this;
	}
	/**
	* Assert the mentioned mail was queued during the fake
	* mode
	*/
	assertQueued(mailConstructor, findFn) {
		if (!this.#queued.find((mail) => {
			if (!findFn) return mail instanceof mailConstructor;
			return mail instanceof mailConstructor && findFn(mail);
		})) throw new AssertionError({ message: `Expected mail "${mailConstructor.name}" was not queued` });
		return this;
	}
	/**
	* Assert the mentioned mail was NOT queued during the fake
	* mode
	*/
	assertNotQueued(mailConstructor, findFn) {
		if (this.#queued.find((mail) => {
			if (!findFn) return mail instanceof mailConstructor;
			return mail instanceof mailConstructor && findFn(mail);
		})) throw new AssertionError({ message: `Unexpected mail "${mailConstructor.name}" was queued` });
		return this;
	}
	assertQueuedCount(mailConstructor, count) {
		if (typeof mailConstructor === "number") {
			const actual = this.#queued.length;
			const expected = mailConstructor;
			if (actual !== expected) throw new AssertionError({
				message: `Expected to queue "${expected}" ${string.pluralize("mail", expected)}, instead received "${actual}" ${string.pluralize("mail", actual)}`,
				actual,
				expected
			});
			return this;
		}
		const actual = this.queued((mail) => mail instanceof mailConstructor).length;
		const expected = count;
		if (actual !== expected) throw new AssertionError({
			message: `Expected "${mailConstructor.name}" to be queued "${expected}" ${string.pluralize("time", expected)}, instead it was queued "${actual}" ${string.pluralize("time", actual)}`,
			actual,
			expected
		});
		return this;
	}
	/**
	* Assert zero emails were queued
	*/
	assertNoneQueued() {
		if (this.#queued.length) throw new AssertionError({
			message: `Expected zero mail to be queued, instead received "${this.#queued.length}" mail`,
			expected: [],
			actual: [this.#queued.map((mail) => mail.constructor.name)]
		});
		return this;
	}
};
/**
* Messages collection to collect messages sent or queued during
* the fake mode
*/
var MessagesCollection = class {
	#sent = [];
	#queued = [];
	/**
	* Default finder to find a message using search options
	*/
	#messageFinder = (message, searchOptions) => {
		if (searchOptions.from && !message.hasFrom(searchOptions.from)) return false;
		if (searchOptions.to && !message.hasTo(searchOptions.to)) return false;
		if (searchOptions.subject && !message.hasSubject(searchOptions.subject)) return false;
		if (searchOptions.attachments && !searchOptions.attachments.every((attachment) => message.hasAttachment(attachment))) return false;
		return true;
	};
	trackSent(message) {
		this.#sent.push(message);
	}
	trackQueued(message) {
		this.#queued.push(message);
	}
	clear() {
		this.#sent = [];
		this.#queued = [];
	}
	/**
	* Returns a list of sent messages captured by the fake mailer
	*/
	sent(filterFn) {
		return filterFn ? this.#sent.filter(filterFn) : this.#sent;
	}
	/**
	* Returns a list of queued messages captured by the fake mailer
	*/
	queued(filterFn) {
		return filterFn ? this.#queued.filter(filterFn) : this.#queued;
	}
	/**
	* Assert the mentioned message was sent during the fake
	* mode
	*/
	assertSent(finder) {
		if (!this.#sent.find(typeof finder === "function" ? finder : (message) => this.#messageFinder(message, finder))) throw new AssertionError({ message: `Expected message was not sent` });
	}
	/**
	* Assert the mentioned message was NOT sent during the fake
	* mode
	*/
	assertNotSent(finder) {
		if (this.#sent.find(typeof finder === "function" ? finder : (message) => this.#messageFinder(message, finder))) throw new AssertionError({ message: `Unexpected message was sent` });
	}
	assertSentCount(finder, count) {
		if (typeof finder === "number") {
			const actual = this.#sent.length;
			const expected = finder;
			if (actual !== expected) throw new AssertionError({
				message: `Expected to send "${expected}" ${string.pluralize("message", expected)}, instead received "${actual}" ${string.pluralize("message", actual)}`,
				actual,
				expected
			});
			return;
		}
		const actual = this.sent(typeof finder === "function" ? finder : (message) => this.#messageFinder(message, finder)).length;
		const expected = count;
		if (actual !== expected) throw new AssertionError({
			message: `Expected to send "${expected}" ${string.pluralize("message", expected)}, instead received "${actual}" ${string.pluralize("message", actual)}`,
			actual,
			expected
		});
	}
	/**
	* Assert zero messages were sent
	*/
	assertNoneSent() {
		if (this.#sent.length) throw new AssertionError({ message: `Expected zero messages to be sent, instead received "${this.#sent.length}" ${string.pluralize("message", this.#sent.length)}` });
	}
	/**
	* Assert the mentioned message was queued during the fake
	* mode
	*/
	assertQueued(finder) {
		if (!this.#queued.find(typeof finder === "function" ? finder : (message) => this.#messageFinder(message, finder))) throw new AssertionError({ message: `Expected message was not queued` });
	}
	/**
	* Assert the mentioned message was NOT queued during the fake
	* mode
	*/
	assertNotQueued(finder) {
		if (this.#queued.find(typeof finder === "function" ? finder : (message) => this.#messageFinder(message, finder))) throw new AssertionError({ message: `Unexpected message was queued` });
	}
	assertQueuedCount(finder, count) {
		if (typeof finder === "number") {
			const actual = this.#queued.length;
			const expected = finder;
			if (actual !== expected) throw new AssertionError({
				message: `Expected to queue "${expected}" ${string.pluralize("message", expected)}, instead received "${actual}" ${string.pluralize("message", actual)}`,
				actual,
				expected
			});
			return;
		}
		const actual = this.queued(typeof finder === "function" ? finder : (message) => this.#messageFinder(message, finder)).length;
		const expected = count;
		if (actual !== expected) throw new AssertionError({
			message: `Expected to queue "${expected}" ${string.pluralize("message", expected)}, instead received "${actual}" ${string.pluralize("message", actual)}`,
			actual,
			expected
		});
	}
	/**
	* Assert zero messages were queued
	*/
	assertNoneQueued() {
		if (this.#queued.length) throw new AssertionError({ message: `Expected zero messages to be queued, instead received "${this.#queued.length}" ${string.pluralize("message", this.#queued.length)}` });
	}
};
/**
* Fake mailer uses the JSON transport to send emails and
* collects them within memory for a better testing
* experience.
*/
var FakeMailer = class extends Mailer {
	mails = new MailsCollection();
	messages = new MessagesCollection();
	#onDispose;
	constructor(name, emitter, config, onDispose) {
		super(name, new JSONTransport(), emitter, config);
		this.#onDispose = onDispose;
		super.setMessenger({ queue: async (mail, sendConfig) => {
			return this.sendCompiled(mail, sendConfig);
		} });
	}
	/**
	* Define the messenger to use for queueing emails.
	* The fake mailer ignores using a custom messenger
	*/
	setMessenger(_) {
		return this;
	}
	/**
	* @inheritdoc
	*/
	async send(callbackOrMail, config) {
		if (callbackOrMail instanceof BaseMail) {
			this.mails.trackSent(callbackOrMail);
			return await super.send(callbackOrMail, config);
		}
		return await super.send((message) => {
			callbackOrMail(message);
			this.messages.trackSent(message);
		}, config);
	}
	/**
	* @inheritdoc
	*/
	async sendLater(callbackOrMail, config) {
		if (callbackOrMail instanceof BaseMail) {
			this.mails.trackQueued(callbackOrMail);
			await callbackOrMail.sendLater(this, config);
			return;
		}
		await super.sendLater((message) => {
			callbackOrMail(message);
			this.messages.trackQueued(message);
		}, config);
	}
	[Symbol.dispose]() {
		this.#onDispose?.();
	}
	async close() {
		this.messages.clear();
		this.mails.clear();
		super.close();
	}
};
//#endregion
//#region src/mail_manager.ts
/**
* Mail manager exposes the API to configure multiple mailers, manage
* their lifecycle and switch between them.
*/
var MailManager = class {
	config;
	#emitter;
	/**
	* Messenger to use on all mailers created
	* using the mail manager
	*/
	#messenger;
	/**
	* Reference to the fake mailer (if any)
	*/
	#fakeMailer;
	/**
	* Cache of mailers
	*/
	#mailersCache = {};
	constructor(emitter, config) {
		this.config = config;
		debug_default("creating mail manager %O", config);
		this.#emitter = emitter;
	}
	/**
	* Configure the messenger for all the mailers managed
	* by the mail manager class.
	*/
	setMessenger(messenger) {
		this.#messenger = messenger;
		Object.keys(this.#mailersCache).forEach((name) => {
			const mailer = this.#mailersCache[name];
			mailer.setMessenger(messenger(mailer));
		});
		return this;
	}
	/**
	* Send email using the default mailer
	*/
	send(callbackOrMail, config) {
		return this.use().send(callbackOrMail, config);
	}
	/**
	* Queue email using the default mailer
	*/
	async sendLater(callbackOrMail, config) {
		await this.use().sendLater(callbackOrMail, config);
	}
	/**
	* Create/use an instance of a known mailer. The mailer
	* instances are cached for the lifecycle of the process
	*/
	use(mailerName) {
		let mailerToUse = mailerName || this.config.default;
		if (!mailerToUse) throw new RuntimeException("Cannot create mailer instance. No default mailer is defined in the config");
		if (!this.config.mailers[mailerToUse]) throw new RuntimeException(`Unknown mailer "${String(mailerToUse)}". Make sure it is configured inside the config file`);
		/**
		* Return fake mailer if one exists
		*/
		if (this.#fakeMailer) return this.#fakeMailer;
		/**
		* Use cached copy if exists
		*/
		const cachedMailer = this.#mailersCache[mailerToUse];
		if (cachedMailer) {
			debug_default("using mailer from cache. name: \"%s\"", cachedMailer);
			return cachedMailer;
		}
		/**
		* Create transport instance using the factory
		*/
		const transportFactory = this.config.mailers[mailerToUse];
		/**
		* Create mailer instance with the transport
		*/
		debug_default("creating mailer transport. name: \"%s\"", mailerToUse);
		const mailer = new Mailer(mailerToUse, transportFactory(), this.#emitter, this.config);
		if (this.#messenger) mailer.setMessenger(this.#messenger(mailer));
		/**
		* Cache it
		*/
		this.#mailersCache[mailerToUse] = mailer;
		return mailer;
	}
	/**
	* Turn on fake mode. After this all calls to "mail.use" will
	* return an instance of the fake mailer
	*/
	fake() {
		this.restore();
		debug_default("creating fake mailer");
		this.#fakeMailer = new FakeMailer("fake", this.#emitter, this.config, () => this.restore());
		return this.#fakeMailer;
	}
	/**
	* Turn off fake mode and restore normal behavior
	*/
	restore() {
		if (this.#fakeMailer) {
			this.#fakeMailer.close();
			this.#fakeMailer = void 0;
			debug_default("restoring mailer fake");
		}
	}
	/**
	* Clear mailer from cache and close its transport
	*/
	async close(mailerName) {
		const mailer = this.#mailersCache[mailerName];
		if (mailer) {
			debug_default("closing mailer %s", mailerName);
			await mailer.close();
			delete this.#mailersCache[mailerName];
		}
	}
	/**
	* Clear all mailers from cache and close their transports
	*/
	async closeAll() {
		await Promise.all(Object.keys(this.#mailersCache).map((mailerName) => this.close(mailerName)));
	}
};
//#endregion
//#region src/define_config.ts
/**
* Helper function to define config for the mail
* service
*/
function defineConfig(config) {
	return configProvider.create(async (app) => {
		const { mailers, default: defaultMailer, ...rest } = config;
		const mailersNames = Object.keys(mailers);
		const transports = {};
		for (let mailerName of mailersNames) {
			const mailerTransport = mailers[mailerName];
			if (typeof mailerTransport === "function") transports[mailerName] = mailerTransport;
			else transports[mailerName] = await mailerTransport.resolver(app);
		}
		return {
			default: defaultMailer,
			mailers: transports,
			...rest
		};
	});
}
/**
* Config helpers to create a reference for inbuilt
* mail transports
*/
const transports = {
	smtp(config) {
		return configProvider.create(async () => {
			const { SMTPTransport } = await import("./src/transports/smtp.js");
			return () => new SMTPTransport(config);
		});
	},
	ses(config) {
		return configProvider.create(async () => {
			const { SESTransport } = await import("./src/transports/ses.js");
			return () => new SESTransport(config);
		});
	},
	mailgun(config) {
		return configProvider.create(async () => {
			const { MailgunTransport } = await import("./src/transports/mailgun.js");
			return () => new MailgunTransport(config);
		});
	},
	sparkpost(config) {
		return configProvider.create(async () => {
			const { SparkPostTransport } = await import("./src/transports/sparkpost.js");
			return () => new SparkPostTransport(config);
		});
	},
	resend(config) {
		return configProvider.create(async () => {
			const { ResendTransport } = await import("./src/transports/resend.js");
			return () => new ResendTransport(config);
		});
	},
	brevo(config) {
		return configProvider.create(async () => {
			const { BrevoTransport } = await import("./src/transports/brevo.js");
			return () => new BrevoTransport(config);
		});
	},
	postmark(config) {
		return configProvider.create(async () => {
			const { PostmarkTransport } = await import("./src/transports/postmark.js");
			return () => new PostmarkTransport(config);
		});
	}
};
//#endregion
export { configure as a, Message as c, FakeMailer as i, transports as n, Mailer as o, MailManager as r, BaseMail as s, defineConfig as t };
