// Package smtp provides a lifecycle-managed SMTP client for sending email // in Einherjar applications. // // # Overview // // [New] returns a [Component] that satisfies [lifecycle.Component] lifecycle // hooks, [observability.Checkable] with degraded priority, and the [Sender] // interface for dispatching email. When [Config.Host] is empty, [New] returns // a no-op implementation that logs a warning and silently discards every message — // email failure must never block a transaction. // // # Lifecycle Registration // // client := smtp.New(logger, cfg) // launcher.Register(client) // health.Register(client) // // # Sending // // Services depend only on [Sender]. Build a [Message] with a pre-rendered body, // then call Send: // // msg := smtp.Message{ // To: []string{"alice@example.com"}, // Subject: "Your receipt", // Body: rendered, // pre-rendered string from Template.Render // ContentType: "text/html", // } // if err := mailer.Send(ctx, msg); err != nil { // return err // } // // # Template Rendering // // [ParseFS] wraps stdlib html/template for HTML email rendering. Rendering is // intentionally separate from Send — callers render to a string and assign it // to Message.Body. This keeps Send unit-testable without template involvement. // // tmpl, err := smtp.ParseFS(os.DirFS("templates"), "*.html") // body, err := tmpl.Render("receipt.html", data) // msg := smtp.Message{Body: body, ContentType: "text/html", ...} // // # Configuration // // All fields are read from environment variables with the EINHERJAR_SMTP_* prefix: // // - EINHERJAR_SMTP_HOST — SMTP server hostname (empty = no-op mode) // - EINHERJAR_SMTP_PORT — default: 587 (STARTTLS) // - EINHERJAR_SMTP_USER — auth username (optional) // - EINHERJAR_SMTP_PASSWORD — auth password (optional) // - EINHERJAR_SMTP_FROM — envelope sender address package smtp