[{"data":1,"prerenderedAt":2135},["ShallowReactive",2],{"navigation_docs":3,"-docs-recipes-send-an-email":493,"-docs-recipes-send-an-email-surround":2130},[4,46,135,218,257,292,355,377,420],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":45},"Getting Started","i-lucide-rocket","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",[10,15,20,25,30,35,40],{"title":11,"path":12,"stem":13,"icon":14},"What is Heximon?","\u002Fdocs\u002Fgetting-started\u002Fintroduction","docs\u002F01.getting-started\u002F01.introduction","i-lucide-info",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","docs\u002F01.getting-started\u002F02.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Quick Start","\u002Fdocs\u002Fgetting-started\u002Fquick-start","docs\u002F01.getting-started\u002F03.quick-start","i-lucide-zap",{"title":26,"path":27,"stem":28,"icon":29},"Core Concepts","\u002Fdocs\u002Fgetting-started\u002Fcore-concepts","docs\u002F01.getting-started\u002F04.core-concepts","i-lucide-shapes",{"title":31,"path":32,"stem":33,"icon":34},"Examples","\u002Fdocs\u002Fgetting-started\u002Fexamples","docs\u002F01.getting-started\u002F05.examples","i-lucide-library",{"title":36,"path":37,"stem":38,"icon":39},"Where Next?","\u002Fdocs\u002Fgetting-started\u002Fwhere-next","docs\u002F01.getting-started\u002F06.where-next","i-lucide-signpost",{"title":41,"path":42,"stem":43,"icon":44},"Troubleshooting","\u002Fdocs\u002Fgetting-started\u002Ftroubleshooting","docs\u002F01.getting-started\u002F07.troubleshooting","i-lucide-life-buoy",false,{"title":47,"icon":48,"path":49,"stem":50,"children":51,"page":45},"Essentials","i-lucide-boxes","\u002Fdocs\u002Fessentials","docs\u002F02.essentials",[52,56,61,66,71,76,81,86,91,96,101,105,110,115,120,125,130],{"title":53,"path":54,"stem":55,"icon":48},"Modules & DI","\u002Fdocs\u002Fessentials\u002Fmodules-and-di","docs\u002F02.essentials\u002F01.modules-and-di",{"title":57,"path":58,"stem":59,"icon":60},"Controllers","\u002Fdocs\u002Fessentials\u002Fcontrollers","docs\u002F02.essentials\u002F02.controllers","i-lucide-route",{"title":62,"path":63,"stem":64,"icon":65},"Validation & DTOs","\u002Fdocs\u002Fessentials\u002Fvalidation","docs\u002F02.essentials\u002F03.validation","i-lucide-shield-check",{"title":67,"path":68,"stem":69,"icon":70},"Contracts","\u002Fdocs\u002Fessentials\u002Fcontracts","docs\u002F02.essentials\u002F04.contracts","i-lucide-file-signature",{"title":72,"path":73,"stem":74,"icon":75},"REST Client","\u002Fdocs\u002Fessentials\u002Fclient","docs\u002F02.essentials\u002F05.client","i-lucide-plug",{"title":77,"path":78,"stem":79,"icon":80},"Database","\u002Fdocs\u002Fessentials\u002Fdatabase","docs\u002F02.essentials\u002F06.database","i-lucide-database",{"title":82,"path":83,"stem":84,"icon":85},"Drizzle ORM","\u002Fdocs\u002Fessentials\u002Fdrizzle","docs\u002F02.essentials\u002F07.drizzle","i-lucide-database-zap",{"title":87,"path":88,"stem":89,"icon":90},"Persistence & Transactions","\u002Fdocs\u002Fessentials\u002Fpersistence","docs\u002F02.essentials\u002F08.persistence","i-lucide-database-backup",{"title":92,"path":93,"stem":94,"icon":95},"Migrations","\u002Fdocs\u002Fessentials\u002Fmigrations","docs\u002F02.essentials\u002F09.migrations","i-lucide-git-branch",{"title":97,"path":98,"stem":99,"icon":100},"Authentication","\u002Fdocs\u002Fessentials\u002Fauthentication","docs\u002F02.essentials\u002F10.authentication","i-lucide-key-round",{"title":102,"path":103,"stem":104,"icon":65},"Permissions & scopes","\u002Fdocs\u002Fessentials\u002Fpermissions","docs\u002F02.essentials\u002F11.permissions",{"title":106,"path":107,"stem":108,"icon":109},"Error Handling","\u002Fdocs\u002Fessentials\u002Ferrors","docs\u002F02.essentials\u002F12.errors","i-lucide-triangle-alert",{"title":111,"path":112,"stem":113,"icon":114},"Testing a Heximon App","\u002Fdocs\u002Fessentials\u002Ftesting","docs\u002F02.essentials\u002F13.testing","i-lucide-flask-conical",{"title":116,"path":117,"stem":118,"icon":119},"Test app harness","\u002Fdocs\u002Fessentials\u002Fcreatetestapp","docs\u002F02.essentials\u002F14.createtestapp","i-lucide-test-tube",{"title":121,"path":122,"stem":123,"icon":124},"Logging & Observability","\u002Fdocs\u002Fessentials\u002Flogging","docs\u002F02.essentials\u002F17.logging","i-lucide-scroll-text",{"title":126,"path":127,"stem":128,"icon":129},"Context & Lifecycle","\u002Fdocs\u002Fessentials\u002Fcontext-and-lifecycle","docs\u002F02.essentials\u002F18.context-and-lifecycle","i-lucide-radio",{"title":131,"path":132,"stem":133,"icon":134},"Project Structure","\u002Fdocs\u002Fessentials\u002Fproject-structure","docs\u002F02.essentials\u002F19.project-structure","i-lucide-folder-tree",{"title":136,"icon":137,"path":138,"stem":139,"children":140,"page":45},"Capabilities","i-lucide-sparkles","\u002Fdocs\u002Fcapabilities","docs\u002F03.capabilities",[141,145,150,155,160,164,169,174,179,184,188,193,198,203,208,213],{"title":142,"path":143,"stem":144,"icon":137},"Overview","\u002Fdocs\u002Fcapabilities\u002Foverview","docs\u002F03.capabilities\u002F01.overview",{"title":146,"path":147,"stem":148,"icon":149},"Events","\u002Fdocs\u002Fcapabilities\u002Fevents","docs\u002F03.capabilities\u002F02.events","i-lucide-radio-tower",{"title":151,"path":152,"stem":153,"icon":154},"Queue","\u002Fdocs\u002Fcapabilities\u002Fbackground-jobs","docs\u002F03.capabilities\u002F03.background-jobs","i-lucide-list-ordered",{"title":156,"path":157,"stem":158,"icon":159},"Scheduled Work","\u002Fdocs\u002Fcapabilities\u002Fscheduled-jobs","docs\u002F03.capabilities\u002F04.scheduled-jobs","i-lucide-clock",{"title":161,"path":162,"stem":163,"icon":85},"Caching","\u002Fdocs\u002Fcapabilities\u002Fcaching","docs\u002F03.capabilities\u002F05.caching",{"title":165,"path":166,"stem":167,"icon":168},"Key\u002FValue Storage","\u002Fdocs\u002Fcapabilities\u002Fkey-value","docs\u002F03.capabilities\u002F06.key-value","i-lucide-hard-drive",{"title":170,"path":171,"stem":172,"icon":173},"Blob Storage","\u002Fdocs\u002Fcapabilities\u002Fblob","docs\u002F03.capabilities\u002F07.blob","i-lucide-folder-open",{"title":175,"path":176,"stem":177,"icon":178},"Health & Readiness","\u002Fdocs\u002Fcapabilities\u002Fhealth","docs\u002F03.capabilities\u002F08.health","i-lucide-heart-pulse",{"title":180,"path":181,"stem":182,"icon":183},"Notifications","\u002Fdocs\u002Fcapabilities\u002Fnotifications","docs\u002F03.capabilities\u002F09.notifications","i-lucide-mail",{"title":185,"path":186,"stem":187,"icon":65},"Idempotency","\u002Fdocs\u002Fcapabilities\u002Fidempotency","docs\u002F03.capabilities\u002F10.idempotency",{"title":189,"path":190,"stem":191,"icon":192},"Security Hardening","\u002Fdocs\u002Fcapabilities\u002Fsecurity","docs\u002F03.capabilities\u002F11.security","i-lucide-shield",{"title":194,"path":195,"stem":196,"icon":197},"Observability & Tracing","\u002Fdocs\u002Fcapabilities\u002Fobservability","docs\u002F03.capabilities\u002F12.observability","i-lucide-activity",{"title":199,"path":200,"stem":201,"icon":202},"OpenAPI","\u002Fdocs\u002Fcapabilities\u002Fopenapi","docs\u002F03.capabilities\u002F13.openapi","i-lucide-book-marked",{"title":204,"path":205,"stem":206,"icon":207},"MCP Server","\u002Fdocs\u002Fcapabilities\u002Fmcp","docs\u002F03.capabilities\u002F14.mcp","i-lucide-bot",{"title":209,"path":210,"stem":211,"icon":212},"Schema DTOs","\u002Fdocs\u002Fcapabilities\u002Fschema","docs\u002F03.capabilities\u002F15.schema","i-lucide-file-json",{"title":214,"path":215,"stem":216,"icon":217},"Branded IDs","\u002Fdocs\u002Fcapabilities\u002Fbranded-ids","docs\u002F03.capabilities\u002F16.branded-ids","i-lucide-tag",{"title":219,"icon":220,"path":221,"stem":222,"children":223,"page":45},"Architecture","i-lucide-building","\u002Fdocs\u002Farchitecture","docs\u002F04.architecture",[224,227,232,237,242,247,252],{"title":142,"path":225,"stem":226,"icon":220},"\u002Fdocs\u002Farchitecture\u002Foverview","docs\u002F04.architecture\u002F01.overview",{"title":228,"path":229,"stem":230,"icon":231},"Domain-Driven Design","\u002Fdocs\u002Farchitecture\u002Fddd","docs\u002F04.architecture\u002F02.ddd","i-lucide-hexagon",{"title":233,"path":234,"stem":235,"icon":236},"CQRS","\u002Fdocs\u002Farchitecture\u002Fcqrs","docs\u002F04.architecture\u002F03.cqrs","i-lucide-split",{"title":238,"path":239,"stem":240,"icon":241},"Repository Pattern","\u002Fdocs\u002Farchitecture\u002Frepository-pattern","docs\u002F04.architecture\u002F04.repository-pattern","i-lucide-archive",{"title":243,"path":244,"stem":245,"icon":246},"Integration Events","\u002Fdocs\u002Farchitecture\u002Fintegration-events","docs\u002F04.architecture\u002F05.integration-events","i-lucide-send-horizontal",{"title":248,"path":249,"stem":250,"icon":251},"Saga Orchestration","\u002Fdocs\u002Farchitecture\u002Fsagas","docs\u002F04.architecture\u002F06.sagas","i-lucide-hourglass",{"title":253,"path":254,"stem":255,"icon":256},"Durable Workflows","\u002Fdocs\u002Farchitecture\u002Fworkflows","docs\u002F04.architecture\u002F07.workflows","i-lucide-workflow",{"title":258,"icon":259,"path":260,"stem":261,"children":262,"page":45},"Realtime","i-lucide-cable","\u002Fdocs\u002Frealtime","docs\u002F05.realtime",[263,266,270,274,279,284,288],{"title":142,"path":264,"stem":265,"icon":259},"\u002Fdocs\u002Frealtime\u002Foverview","docs\u002F05.realtime\u002F01.overview",{"title":267,"path":268,"stem":269,"icon":129},"WebSockets","\u002Fdocs\u002Frealtime\u002Fwebsockets","docs\u002F05.realtime\u002F02.websockets",{"title":271,"path":272,"stem":273,"icon":70},"WebSocket Contracts","\u002Fdocs\u002Frealtime\u002Fwebsocket-contracts","docs\u002F05.realtime\u002F03.websocket-contracts",{"title":275,"path":276,"stem":277,"icon":278},"Server-Sent Events","\u002Fdocs\u002Frealtime\u002Fserver-sent-events","docs\u002F05.realtime\u002F04.server-sent-events","i-lucide-rss",{"title":280,"path":281,"stem":282,"icon":283},"Typed Clients","\u002Fdocs\u002Frealtime\u002Ftyped-clients","docs\u002F05.realtime\u002F05.typed-clients","i-lucide-plug-zap",{"title":285,"path":286,"stem":287,"icon":259},"AsyncAPI","\u002Fdocs\u002Frealtime\u002Fasyncapi","docs\u002F05.realtime\u002F06.asyncapi",{"title":289,"path":290,"stem":291,"icon":80},"Durable Objects","\u002Fdocs\u002Frealtime\u002Fdurable-objects","docs\u002F05.realtime\u002F07.durable-objects",{"title":293,"icon":294,"path":295,"stem":296,"children":297,"page":45},"Deploy","i-lucide-cloud-upload","\u002Fdocs\u002Fdeploy","docs\u002F06.deploy",[298,301,306,311,316,321,326,331,336,341,345,350],{"title":142,"path":299,"stem":300,"icon":6},"\u002Fdocs\u002Fdeploy\u002Foverview","docs\u002F06.deploy\u002F01.overview",{"title":302,"path":303,"stem":304,"icon":305},"Node","\u002Fdocs\u002Fdeploy\u002Fnode","docs\u002F06.deploy\u002F02.node","la:node-js",{"title":307,"path":308,"stem":309,"icon":310},"Cloudflare","\u002Fdocs\u002Fdeploy\u002Fcloudflare","docs\u002F06.deploy\u002F03.cloudflare","simple-icons:cloudflare",{"title":312,"path":313,"stem":314,"icon":315},"Vercel","\u002Fdocs\u002Fdeploy\u002Fvercel","docs\u002F06.deploy\u002F04.vercel","ri:vercel-fill",{"title":317,"path":318,"stem":319,"icon":320},"Netlify","\u002Fdocs\u002Fdeploy\u002Fnetlify","docs\u002F06.deploy\u002F05.netlify","i-simple-icons-netlify",{"title":322,"path":323,"stem":324,"icon":325},"AWS Lambda","\u002Fdocs\u002Fdeploy\u002Faws-lambda","docs\u002F06.deploy\u002F06.aws-lambda","hugeicons:aws-lambda",{"title":327,"path":328,"stem":329,"icon":330},"Deno Deploy","\u002Fdocs\u002Fdeploy\u002Fdeno-deploy","docs\u002F06.deploy\u002F07.deno-deploy","simple-icons:denodeploy",{"title":332,"path":333,"stem":334,"icon":335},"Nitro","\u002Fdocs\u002Fdeploy\u002Fnitro","docs\u002F06.deploy\u002F08.nitro","i-unjs-nitro",{"title":337,"path":338,"stem":339,"icon":340},"Nuxt","\u002Fdocs\u002Fdeploy\u002Fnuxt","docs\u002F06.deploy\u002F09.nuxt","i-devicon-nuxt",{"title":342,"path":343,"stem":344,"icon":236},"Mixed-runtime split","\u002Fdocs\u002Fdeploy\u002Fmixed-runtime-split","docs\u002F06.deploy\u002F10.mixed-runtime-split",{"title":346,"path":347,"stem":348,"icon":349},"Hosts","\u002Fdocs\u002Fdeploy\u002Fhosts","docs\u002F06.deploy\u002F11.hosts","i-lucide-map",{"title":351,"path":352,"stem":353,"icon":354},"Cross-service events","\u002Fdocs\u002Fdeploy\u002Fcross-service-events","docs\u002F06.deploy\u002F12.cross-service-events","i-lucide-network",{"title":356,"icon":357,"path":358,"stem":359,"children":360,"page":45},"CLI","i-lucide-terminal","\u002Fdocs\u002Fcli","docs\u002F07.cli",[361,365,369,373],{"title":362,"path":363,"stem":364,"icon":357},"The heximon CLI","\u002Fdocs\u002Fcli\u002Foverview","docs\u002F07.cli\u002F01.overview",{"title":366,"path":367,"stem":368,"icon":137},"Create a New App","\u002Fdocs\u002Fcli\u002Fcreate","docs\u002F07.cli\u002F02.create",{"title":370,"path":371,"stem":372,"icon":95},"Migrations from the CLI","\u002Fdocs\u002Fcli\u002Fmigrations","docs\u002F07.cli\u002F03.migrations",{"title":374,"path":375,"stem":376,"icon":6},"Deploying a Heximon app","\u002Fdocs\u002Fcli\u002Fdeploy","docs\u002F07.cli\u002F04.deploy",{"title":378,"icon":379,"path":380,"stem":381,"children":382,"page":45},"Under the Hood","i-lucide-wrench","\u002Fdocs\u002Funder-the-hood","docs\u002F08.under-the-hood",[383,388,393,397,402,407,411,415],{"title":384,"path":385,"stem":386,"icon":387},"How the Compiler Works","\u002Fdocs\u002Funder-the-hood\u002Fhow-the-compiler-works","docs\u002F08.under-the-hood\u002F01.how-the-compiler-works","i-lucide-cog",{"title":389,"path":390,"stem":391,"icon":392},"Compiler","\u002Fdocs\u002Funder-the-hood\u002Fextending-the-compiler","docs\u002F08.under-the-hood\u002F02.extending-the-compiler","i-lucide-cpu",{"title":394,"path":395,"stem":396,"icon":357},"Compiler Diagnostics","\u002Fdocs\u002Funder-the-hood\u002Fcompiler-diagnostics","docs\u002F08.under-the-hood\u002F03.compiler-diagnostics",{"title":398,"path":399,"stem":400,"icon":401},"Library Mode","\u002Fdocs\u002Funder-the-hood\u002Flibrary-mode","docs\u002F08.under-the-hood\u002F04.library-mode","i-lucide-package",{"title":403,"path":404,"stem":405,"icon":406},"Other bundlers","\u002Fdocs\u002Funder-the-hood\u002Falternate-bundlers","docs\u002F08.under-the-hood\u002F05.alternate-bundlers","i-lucide-blocks",{"title":408,"path":409,"stem":410,"icon":401},"Packages","\u002Fdocs\u002Funder-the-hood\u002Fpackages","docs\u002F08.under-the-hood\u002F06.packages",{"title":412,"path":413,"stem":414,"icon":207},"AI Skills","\u002Fdocs\u002Funder-the-hood\u002Fai-and-agents","docs\u002F08.under-the-hood\u002F07.ai-and-agents",{"title":416,"path":417,"stem":418,"icon":419},"Advanced DI","\u002Fdocs\u002Funder-the-hood\u002Fadvanced-di","docs\u002F08.under-the-hood\u002F08.advanced-di","i-lucide-git-fork",{"title":421,"icon":422,"path":423,"stem":424,"children":425,"page":45},"Recipes","i-lucide-book-open-check","\u002Fdocs\u002Frecipes","docs\u002F09.recipes",[426,431,436,440,444,448,452,456,460,465,469,474,479,484,488],{"title":427,"path":428,"stem":429,"icon":430},"Send an Email","\u002Fdocs\u002Frecipes\u002Fsend-an-email","docs\u002F09.recipes\u002F01.send-an-email","i-lucide-send",{"title":432,"path":433,"stem":434,"icon":435},"Upload a File","\u002Fdocs\u002Frecipes\u002Fupload-a-file","docs\u002F09.recipes\u002F02.upload-a-file","i-lucide-upload",{"title":437,"path":438,"stem":439,"icon":24},"Cache an Expensive Call","\u002Fdocs\u002Frecipes\u002Fcache-an-expensive-call","docs\u002F09.recipes\u002F03.cache-an-expensive-call",{"title":441,"path":442,"stem":443,"icon":154},"Paginate a List","\u002Fdocs\u002Frecipes\u002Fpaginate-a-list","docs\u002F09.recipes\u002F04.paginate-a-list",{"title":445,"path":446,"stem":447,"icon":65},"Protect Admin Routes","\u002Fdocs\u002Frecipes\u002Fprotect-admin-routes","docs\u002F09.recipes\u002F05.protect-admin-routes",{"title":449,"path":450,"stem":451,"icon":178},"Health Probes and a Nightly Job","\u002Fdocs\u002Frecipes\u002Fhealth-probes-and-a-nightly-job","docs\u002F09.recipes\u002F06.health-probes-and-a-nightly-job",{"title":453,"path":454,"stem":455,"icon":80},"Build a CRUD API","\u002Fdocs\u002Frecipes\u002Fcrud-api","docs\u002F09.recipes\u002F07.crud-api",{"title":457,"path":458,"stem":459,"icon":100},"Add Authentication","\u002Fdocs\u002Frecipes\u002Fadd-authentication","docs\u002F09.recipes\u002F08.add-authentication",{"title":461,"path":462,"stem":463,"icon":464},"Background Jobs","\u002Fdocs\u002Frecipes\u002Fbackground-jobs","docs\u002F09.recipes\u002F09.background-jobs","i-lucide-timer",{"title":466,"path":467,"stem":468,"icon":149},"Decouple Features with Events","\u002Fdocs\u002Frecipes\u002Fevent-driven-module","docs\u002F09.recipes\u002F10.event-driven-module",{"title":470,"path":471,"stem":472,"icon":473},"Build a DDD Aggregate","\u002Fdocs\u002Frecipes\u002Fddd-aggregate","docs\u002F09.recipes\u002F11.ddd-aggregate","i-lucide-box",{"title":475,"path":476,"stem":477,"icon":478},"Publish a Feature Package","\u002Fdocs\u002Frecipes\u002Ffeature-package","docs\u002F09.recipes\u002F12.feature-package","i-lucide-package-open",{"title":480,"path":481,"stem":482,"icon":483},"Reliable Integration Events","\u002Fdocs\u002Frecipes\u002Freliable-integration-events","docs\u002F09.recipes\u002F13.reliable-integration-events","i-lucide-inbox",{"title":485,"path":486,"stem":487,"icon":95},"Reactive Choreography","\u002Fdocs\u002Frecipes\u002Freactive-choreography","docs\u002F09.recipes\u002F14.reactive-choreography",{"title":489,"path":490,"stem":491,"icon":492},"Build a Production CRUD API","\u002Fdocs\u002Frecipes\u002Fproduction-crud-api","docs\u002F09.recipes\u002F15.production-crud-api","i-lucide-layers",{"id":494,"title":427,"body":495,"description":2123,"extension":2124,"links":2125,"meta":2126,"navigation":2127,"path":428,"seo":2128,"stem":429,"__hash__":2129},"docs\u002Fdocs\u002F09.recipes\u002F01.send-an-email.md",{"type":496,"value":497,"toc":2116},"minimark",[498,507,514,519,524,991,1094,1108,1112,1129,1440,1443,1567,1571,1579,1789,1793,1811,1832,2046,2059,2071,2075,2112],[499,500,501,502,506],"p",{},"Send a welcome email from a service, without hard-coding a provider into the code that composes it. You\ninject the abstract ",[503,504,505],"code",{},"EmailTransport"," token; the module decides whether that resolves to an in-memory\ncapture transport (tests, local dev) or Resend in production.",[499,508,509,510,513],{},"The full picture — SMS, push, templating, suppression, orchestration across channels — is the\n",[511,512,180],"a",{"href":181}," page; this recipe is the shortest path to one send.",[515,516,518],"h2",{"id":517},"_1-bind-a-transport-in-a-module","1. Bind a transport in a module",[499,520,521,523],{},[503,522,505],{}," is an abstract class, so it's a DI token — the compiler wires whichever concrete class\nsatisfies it. Provide a capture transport (records sends in memory, never calls a real provider) so the\nrecipe runs with no API key and no network:",[525,526,532],"pre",{"className":527,"code":528,"filename":529,"language":530,"meta":531,"style":531},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { uuid } from \"@heximon\u002Fruntime\";\nimport type { TransportResult } from \"@heximon\u002Fnotifications\";\nimport { type EmailMessage, EmailTransport } from \"@heximon\u002Fnotifications\u002Femail\";\n\n\u002F** Records every email in memory instead of delivering it. *\u002F\nexport class CaptureEmailTransport extends EmailTransport {\n  private readonly captured: { id: string; subject: string; content: string }[] = [];\n\n  public sent(): readonly { id: string; subject: string; content: string }[] {\n    return [...this.captured];\n  }\n\n  protected override async transport(message: EmailMessage): Promise\u003CTransportResult> {\n    const id = uuid.v7();\n    this.captured.push({\n      id,\n      subject: message.subject,\n      content: message.getContent(), \u002F\u002F renders the Mustache placeholders\n    });\n    return { messageId: id, timestamp: new Date() };\n  }\n}\n","src\u002Fmail\u002Fcapture-email-transport.ts","ts","",[503,533,534,570,596,627,634,641,663,721,726,771,791,797,802,845,870,887,896,914,936,947,980,985],{"__ignoreMap":531},[535,536,539,543,547,551,554,557,560,564,567],"span",{"class":537,"line":538},"line",1,[535,540,542],{"class":541},"s7zQu","import",[535,544,546],{"class":545},"sMK4o"," {",[535,548,550],{"class":549},"sTEyZ"," uuid",[535,552,553],{"class":545}," }",[535,555,556],{"class":541}," from",[535,558,559],{"class":545}," \"",[535,561,563],{"class":562},"sfazB","@heximon\u002Fruntime",[535,565,566],{"class":545},"\"",[535,568,569],{"class":545},";\n",[535,571,573,575,578,580,583,585,587,589,592,594],{"class":537,"line":572},2,[535,574,542],{"class":541},[535,576,577],{"class":541}," type",[535,579,546],{"class":545},[535,581,582],{"class":549}," TransportResult",[535,584,553],{"class":545},[535,586,556],{"class":541},[535,588,559],{"class":545},[535,590,591],{"class":562},"@heximon\u002Fnotifications",[535,593,566],{"class":545},[535,595,569],{"class":545},[535,597,599,601,603,605,608,611,614,616,618,620,623,625],{"class":537,"line":598},3,[535,600,542],{"class":541},[535,602,546],{"class":545},[535,604,577],{"class":541},[535,606,607],{"class":549}," EmailMessage",[535,609,610],{"class":545},",",[535,612,613],{"class":549}," EmailTransport",[535,615,553],{"class":545},[535,617,556],{"class":541},[535,619,559],{"class":545},[535,621,622],{"class":562},"@heximon\u002Fnotifications\u002Femail",[535,624,566],{"class":545},[535,626,569],{"class":545},[535,628,630],{"class":537,"line":629},4,[535,631,633],{"emptyLinePlaceholder":632},true,"\n",[535,635,637],{"class":537,"line":636},5,[535,638,640],{"class":639},"sHwdD","\u002F** Records every email in memory instead of delivering it. *\u002F\n",[535,642,644,647,651,655,658,660],{"class":537,"line":643},6,[535,645,646],{"class":541},"export",[535,648,650],{"class":649},"spNyl"," class",[535,652,654],{"class":653},"sBMFI"," CaptureEmailTransport",[535,656,657],{"class":649}," extends",[535,659,613],{"class":653},[535,661,662],{"class":545}," {\n",[535,664,666,669,672,676,679,681,684,686,689,692,695,697,699,701,704,706,708,710,713,716,719],{"class":537,"line":665},7,[535,667,668],{"class":649},"  private",[535,670,671],{"class":649}," readonly",[535,673,675],{"class":674},"swJcz"," captured",[535,677,678],{"class":545},":",[535,680,546],{"class":545},[535,682,683],{"class":674}," id",[535,685,678],{"class":545},[535,687,688],{"class":653}," string",[535,690,691],{"class":545},";",[535,693,694],{"class":674}," subject",[535,696,678],{"class":545},[535,698,688],{"class":653},[535,700,691],{"class":545},[535,702,703],{"class":674}," content",[535,705,678],{"class":545},[535,707,688],{"class":653},[535,709,553],{"class":545},[535,711,712],{"class":549},"[] ",[535,714,715],{"class":545},"=",[535,717,718],{"class":549}," []",[535,720,569],{"class":545},[535,722,724],{"class":537,"line":723},8,[535,725,633],{"emptyLinePlaceholder":632},[535,727,729,732,735,738,740,742,744,746,748,750,752,754,756,758,760,762,764,766,768],{"class":537,"line":728},9,[535,730,731],{"class":649},"  public",[535,733,734],{"class":674}," sent",[535,736,737],{"class":545},"():",[535,739,671],{"class":649},[535,741,546],{"class":545},[535,743,683],{"class":653},[535,745,678],{"class":545},[535,747,688],{"class":549},[535,749,691],{"class":545},[535,751,694],{"class":653},[535,753,678],{"class":545},[535,755,688],{"class":549},[535,757,691],{"class":545},[535,759,703],{"class":653},[535,761,678],{"class":545},[535,763,688],{"class":549},[535,765,553],{"class":545},[535,767,712],{"class":549},[535,769,770],{"class":545},"{\n",[535,772,774,777,780,783,786,789],{"class":537,"line":773},10,[535,775,776],{"class":541},"    return",[535,778,779],{"class":674}," [",[535,781,782],{"class":545},"...this.",[535,784,785],{"class":549},"captured",[535,787,788],{"class":674},"]",[535,790,569],{"class":545},[535,792,794],{"class":537,"line":793},11,[535,795,796],{"class":545},"  }\n",[535,798,800],{"class":537,"line":799},12,[535,801,633],{"emptyLinePlaceholder":632},[535,803,805,808,811,814,817,820,824,826,828,831,834,837,840,843],{"class":537,"line":804},13,[535,806,807],{"class":649},"  protected",[535,809,810],{"class":649}," override",[535,812,813],{"class":649}," async",[535,815,816],{"class":674}," transport",[535,818,819],{"class":545},"(",[535,821,823],{"class":822},"sHdIc","message",[535,825,678],{"class":545},[535,827,607],{"class":653},[535,829,830],{"class":545},"):",[535,832,833],{"class":653}," Promise",[535,835,836],{"class":545},"\u003C",[535,838,839],{"class":653},"TransportResult",[535,841,842],{"class":545},">",[535,844,662],{"class":545},[535,846,848,851,853,856,858,861,865,868],{"class":537,"line":847},14,[535,849,850],{"class":649},"    const",[535,852,683],{"class":549},[535,854,855],{"class":545}," =",[535,857,550],{"class":549},[535,859,860],{"class":545},".",[535,862,864],{"class":863},"s2Zo4","v7",[535,866,867],{"class":674},"()",[535,869,569],{"class":545},[535,871,873,876,878,880,883,885],{"class":537,"line":872},15,[535,874,875],{"class":545},"    this.",[535,877,785],{"class":549},[535,879,860],{"class":545},[535,881,882],{"class":863},"push",[535,884,819],{"class":674},[535,886,770],{"class":545},[535,888,890,893],{"class":537,"line":889},16,[535,891,892],{"class":549},"      id",[535,894,895],{"class":545},",\n",[535,897,899,902,904,907,909,912],{"class":537,"line":898},17,[535,900,901],{"class":674},"      subject",[535,903,678],{"class":545},[535,905,906],{"class":549}," message",[535,908,860],{"class":545},[535,910,911],{"class":549},"subject",[535,913,895],{"class":545},[535,915,917,920,922,924,926,929,931,933],{"class":537,"line":916},18,[535,918,919],{"class":674},"      content",[535,921,678],{"class":545},[535,923,906],{"class":549},[535,925,860],{"class":545},[535,927,928],{"class":863},"getContent",[535,930,867],{"class":674},[535,932,610],{"class":545},[535,934,935],{"class":639}," \u002F\u002F renders the Mustache placeholders\n",[535,937,939,942,945],{"class":537,"line":938},19,[535,940,941],{"class":545},"    }",[535,943,944],{"class":674},")",[535,946,569],{"class":545},[535,948,950,952,954,957,959,961,963,966,968,971,974,977],{"class":537,"line":949},20,[535,951,776],{"class":541},[535,953,546],{"class":545},[535,955,956],{"class":674}," messageId",[535,958,678],{"class":545},[535,960,683],{"class":549},[535,962,610],{"class":545},[535,964,965],{"class":674}," timestamp",[535,967,678],{"class":545},[535,969,970],{"class":545}," new",[535,972,973],{"class":863}," Date",[535,975,976],{"class":674},"() ",[535,978,979],{"class":545},"};\n",[535,981,983],{"class":537,"line":982},21,[535,984,796],{"class":545},[535,986,988],{"class":537,"line":987},22,[535,989,990],{"class":545},"}\n",[525,992,995],{"className":527,"code":993,"filename":994,"language":530,"meta":531,"style":531},"import { Module } from \"@heximon\u002Fruntime\";\nimport { CaptureEmailTransport } from \".\u002Fcapture-email-transport\";\n\nexport class MailModule extends Module({\n  providers: [CaptureEmailTransport],\n  exports: [CaptureEmailTransport],\n}) {}\n","src\u002Fmail\u002Fmail.module.ts",[503,996,997,1018,1039,1043,1060,1072,1083],{"__ignoreMap":531},[535,998,999,1001,1003,1006,1008,1010,1012,1014,1016],{"class":537,"line":538},[535,1000,542],{"class":541},[535,1002,546],{"class":545},[535,1004,1005],{"class":549}," Module",[535,1007,553],{"class":545},[535,1009,556],{"class":541},[535,1011,559],{"class":545},[535,1013,563],{"class":562},[535,1015,566],{"class":545},[535,1017,569],{"class":545},[535,1019,1020,1022,1024,1026,1028,1030,1032,1035,1037],{"class":537,"line":572},[535,1021,542],{"class":541},[535,1023,546],{"class":545},[535,1025,654],{"class":549},[535,1027,553],{"class":545},[535,1029,556],{"class":541},[535,1031,559],{"class":545},[535,1033,1034],{"class":562},".\u002Fcapture-email-transport",[535,1036,566],{"class":545},[535,1038,569],{"class":545},[535,1040,1041],{"class":537,"line":598},[535,1042,633],{"emptyLinePlaceholder":632},[535,1044,1045,1047,1049,1052,1054,1056,1058],{"class":537,"line":629},[535,1046,646],{"class":541},[535,1048,650],{"class":649},[535,1050,1051],{"class":653}," MailModule",[535,1053,657],{"class":649},[535,1055,1005],{"class":863},[535,1057,819],{"class":549},[535,1059,770],{"class":545},[535,1061,1062,1065,1067,1070],{"class":537,"line":636},[535,1063,1064],{"class":674},"  providers",[535,1066,678],{"class":545},[535,1068,1069],{"class":549}," [CaptureEmailTransport]",[535,1071,895],{"class":545},[535,1073,1074,1077,1079,1081],{"class":537,"line":643},[535,1075,1076],{"class":674},"  exports",[535,1078,678],{"class":545},[535,1080,1069],{"class":549},[535,1082,895],{"class":545},[535,1084,1085,1088,1091],{"class":537,"line":665},[535,1086,1087],{"class":545},"}",[535,1089,1090],{"class":549},") ",[535,1092,1093],{"class":545},"{}\n",[499,1095,1096,1097,1100,1101,1103,1104,1107],{},"Because ",[503,1098,1099],{},"CaptureEmailTransport extends EmailTransport",", it satisfies the abstract token — any consumer\nthat injects ",[503,1102,505],{}," gets it, and any consumer that injects the concrete ",[503,1105,1106],{},"CaptureEmailTransport","\ncan also read its capture store back.",[515,1109,1111],{"id":1110},"_2-compose-and-send-the-message","2. Compose and send the message",[499,1113,1114,1117,1118,1121,1122,1124,1125,1128],{},[503,1115,1116],{},"EmailMessage"," takes one props object — an HTML ",[503,1119,1120],{},"content"," with Mustache placeholders and the values to\nfill them. Inject ",[503,1123,505],{}," and call ",[503,1126,1127],{},"send()","; it never throws, so a provider outage can't take\ndown the request that triggered it.",[525,1130,1133],{"className":527,"code":1131,"filename":1132,"language":530,"meta":531,"style":531},"import type { DeliveryResult } from \"@heximon\u002Fnotifications\";\nimport { EmailMessage, EmailTransport } from \"@heximon\u002Fnotifications\u002Femail\";\n\nexport class WelcomeEmailService {\n  public constructor(private readonly mail: EmailTransport) {}\n\n  public async sendTo(user: { name: string; email: string }): Promise\u003CDeliveryResult> {\n    const message = new EmailMessage({\n      from: { email: \"hello@example.com\" },\n      to: { name: user.name, email: user.email },\n      subject: \"Welcome, {{ name }}!\",\n      content: \"\u003Cp>Thanks for joining, {{ name }}.\u003C\u002Fp>\",\n      placeholders: { name: user.name },\n    });\n    return this.mail.send(message);\n  }\n}\n","src\u002Fmail\u002Fwelcome-email.service.ts",[503,1134,1135,1158,1182,1186,1197,1223,1227,1275,1291,1314,1350,1365,1380,1401,1409,1432,1436],{"__ignoreMap":531},[535,1136,1137,1139,1141,1143,1146,1148,1150,1152,1154,1156],{"class":537,"line":538},[535,1138,542],{"class":541},[535,1140,577],{"class":541},[535,1142,546],{"class":545},[535,1144,1145],{"class":549}," DeliveryResult",[535,1147,553],{"class":545},[535,1149,556],{"class":541},[535,1151,559],{"class":545},[535,1153,591],{"class":562},[535,1155,566],{"class":545},[535,1157,569],{"class":545},[535,1159,1160,1162,1164,1166,1168,1170,1172,1174,1176,1178,1180],{"class":537,"line":572},[535,1161,542],{"class":541},[535,1163,546],{"class":545},[535,1165,607],{"class":549},[535,1167,610],{"class":545},[535,1169,613],{"class":549},[535,1171,553],{"class":545},[535,1173,556],{"class":541},[535,1175,559],{"class":545},[535,1177,622],{"class":562},[535,1179,566],{"class":545},[535,1181,569],{"class":545},[535,1183,1184],{"class":537,"line":598},[535,1185,633],{"emptyLinePlaceholder":632},[535,1187,1188,1190,1192,1195],{"class":537,"line":629},[535,1189,646],{"class":541},[535,1191,650],{"class":649},[535,1193,1194],{"class":653}," WelcomeEmailService",[535,1196,662],{"class":545},[535,1198,1199,1201,1204,1206,1209,1211,1214,1216,1218,1220],{"class":537,"line":636},[535,1200,731],{"class":649},[535,1202,1203],{"class":649}," constructor",[535,1205,819],{"class":545},[535,1207,1208],{"class":649},"private",[535,1210,671],{"class":649},[535,1212,1213],{"class":822}," mail",[535,1215,678],{"class":545},[535,1217,613],{"class":653},[535,1219,944],{"class":545},[535,1221,1222],{"class":545}," {}\n",[535,1224,1225],{"class":537,"line":643},[535,1226,633],{"emptyLinePlaceholder":632},[535,1228,1229,1231,1233,1236,1238,1241,1243,1245,1248,1250,1252,1254,1257,1259,1261,1264,1266,1268,1271,1273],{"class":537,"line":665},[535,1230,731],{"class":649},[535,1232,813],{"class":649},[535,1234,1235],{"class":674}," sendTo",[535,1237,819],{"class":545},[535,1239,1240],{"class":822},"user",[535,1242,678],{"class":545},[535,1244,546],{"class":545},[535,1246,1247],{"class":674}," name",[535,1249,678],{"class":545},[535,1251,688],{"class":653},[535,1253,691],{"class":545},[535,1255,1256],{"class":674}," email",[535,1258,678],{"class":545},[535,1260,688],{"class":653},[535,1262,1263],{"class":545}," }):",[535,1265,833],{"class":653},[535,1267,836],{"class":545},[535,1269,1270],{"class":653},"DeliveryResult",[535,1272,842],{"class":545},[535,1274,662],{"class":545},[535,1276,1277,1279,1281,1283,1285,1287,1289],{"class":537,"line":723},[535,1278,850],{"class":649},[535,1280,906],{"class":549},[535,1282,855],{"class":545},[535,1284,970],{"class":545},[535,1286,607],{"class":863},[535,1288,819],{"class":674},[535,1290,770],{"class":545},[535,1292,1293,1296,1298,1300,1302,1304,1306,1309,1311],{"class":537,"line":728},[535,1294,1295],{"class":674},"      from",[535,1297,678],{"class":545},[535,1299,546],{"class":545},[535,1301,1256],{"class":674},[535,1303,678],{"class":545},[535,1305,559],{"class":545},[535,1307,1308],{"class":562},"hello@example.com",[535,1310,566],{"class":545},[535,1312,1313],{"class":545}," },\n",[535,1315,1316,1319,1321,1323,1325,1327,1330,1332,1335,1337,1339,1341,1343,1345,1348],{"class":537,"line":773},[535,1317,1318],{"class":674},"      to",[535,1320,678],{"class":545},[535,1322,546],{"class":545},[535,1324,1247],{"class":674},[535,1326,678],{"class":545},[535,1328,1329],{"class":549}," user",[535,1331,860],{"class":545},[535,1333,1334],{"class":549},"name",[535,1336,610],{"class":545},[535,1338,1256],{"class":674},[535,1340,678],{"class":545},[535,1342,1329],{"class":549},[535,1344,860],{"class":545},[535,1346,1347],{"class":549},"email",[535,1349,1313],{"class":545},[535,1351,1352,1354,1356,1358,1361,1363],{"class":537,"line":793},[535,1353,901],{"class":674},[535,1355,678],{"class":545},[535,1357,559],{"class":545},[535,1359,1360],{"class":562},"Welcome, {{ name }}!",[535,1362,566],{"class":545},[535,1364,895],{"class":545},[535,1366,1367,1369,1371,1373,1376,1378],{"class":537,"line":799},[535,1368,919],{"class":674},[535,1370,678],{"class":545},[535,1372,559],{"class":545},[535,1374,1375],{"class":562},"\u003Cp>Thanks for joining, {{ name }}.\u003C\u002Fp>",[535,1377,566],{"class":545},[535,1379,895],{"class":545},[535,1381,1382,1385,1387,1389,1391,1393,1395,1397,1399],{"class":537,"line":804},[535,1383,1384],{"class":674},"      placeholders",[535,1386,678],{"class":545},[535,1388,546],{"class":545},[535,1390,1247],{"class":674},[535,1392,678],{"class":545},[535,1394,1329],{"class":549},[535,1396,860],{"class":545},[535,1398,1334],{"class":549},[535,1400,1313],{"class":545},[535,1402,1403,1405,1407],{"class":537,"line":847},[535,1404,941],{"class":545},[535,1406,944],{"class":674},[535,1408,569],{"class":545},[535,1410,1411,1413,1416,1419,1421,1424,1426,1428,1430],{"class":537,"line":872},[535,1412,776],{"class":541},[535,1414,1415],{"class":545}," this.",[535,1417,1418],{"class":549},"mail",[535,1420,860],{"class":545},[535,1422,1423],{"class":863},"send",[535,1425,819],{"class":674},[535,1427,823],{"class":549},[535,1429,944],{"class":674},[535,1431,569],{"class":545},[535,1433,1434],{"class":537,"line":889},[535,1435,796],{"class":545},[535,1437,1438],{"class":537,"line":898},[535,1439,990],{"class":545},[499,1441,1442],{},"List the service alongside the transport so it can inject it:",[525,1444,1446],{"className":527,"code":1445,"filename":994,"language":530,"meta":531,"style":531},"import { Module } from \"@heximon\u002Fruntime\";\nimport { CaptureEmailTransport } from \".\u002Fcapture-email-transport\";\nimport { WelcomeEmailService } from \".\u002Fwelcome-email.service\";\n\nexport class MailModule extends Module({\n  providers: [CaptureEmailTransport, WelcomeEmailService],\n  exports: [CaptureEmailTransport, WelcomeEmailService],\n}) {}\n",[503,1447,1448,1468,1488,1509,1513,1529,1545,1559],{"__ignoreMap":531},[535,1449,1450,1452,1454,1456,1458,1460,1462,1464,1466],{"class":537,"line":538},[535,1451,542],{"class":541},[535,1453,546],{"class":545},[535,1455,1005],{"class":549},[535,1457,553],{"class":545},[535,1459,556],{"class":541},[535,1461,559],{"class":545},[535,1463,563],{"class":562},[535,1465,566],{"class":545},[535,1467,569],{"class":545},[535,1469,1470,1472,1474,1476,1478,1480,1482,1484,1486],{"class":537,"line":572},[535,1471,542],{"class":541},[535,1473,546],{"class":545},[535,1475,654],{"class":549},[535,1477,553],{"class":545},[535,1479,556],{"class":541},[535,1481,559],{"class":545},[535,1483,1034],{"class":562},[535,1485,566],{"class":545},[535,1487,569],{"class":545},[535,1489,1490,1492,1494,1496,1498,1500,1502,1505,1507],{"class":537,"line":598},[535,1491,542],{"class":541},[535,1493,546],{"class":545},[535,1495,1194],{"class":549},[535,1497,553],{"class":545},[535,1499,556],{"class":541},[535,1501,559],{"class":545},[535,1503,1504],{"class":562},".\u002Fwelcome-email.service",[535,1506,566],{"class":545},[535,1508,569],{"class":545},[535,1510,1511],{"class":537,"line":629},[535,1512,633],{"emptyLinePlaceholder":632},[535,1514,1515,1517,1519,1521,1523,1525,1527],{"class":537,"line":636},[535,1516,646],{"class":541},[535,1518,650],{"class":649},[535,1520,1051],{"class":653},[535,1522,657],{"class":649},[535,1524,1005],{"class":863},[535,1526,819],{"class":549},[535,1528,770],{"class":545},[535,1530,1531,1533,1535,1538,1540,1543],{"class":537,"line":643},[535,1532,1064],{"class":674},[535,1534,678],{"class":545},[535,1536,1537],{"class":549}," [CaptureEmailTransport",[535,1539,610],{"class":545},[535,1541,1542],{"class":549}," WelcomeEmailService]",[535,1544,895],{"class":545},[535,1546,1547,1549,1551,1553,1555,1557],{"class":537,"line":665},[535,1548,1076],{"class":674},[535,1550,678],{"class":545},[535,1552,1537],{"class":549},[535,1554,610],{"class":545},[535,1556,1542],{"class":549},[535,1558,895],{"class":545},[535,1560,1561,1563,1565],{"class":537,"line":723},[535,1562,1087],{"class":545},[535,1564,1090],{"class":549},[535,1566,1093],{"class":545},[515,1568,1570],{"id":1569},"_3-read-the-result","3. Read the result",[499,1572,1573,1575,1576,1578],{},[503,1574,1127],{}," returns a ",[503,1577,1270],{}," instead of throwing, so the caller decides what a failure means —\nlog it, retry it, or surface it — rather than the transport deciding for you:",[525,1580,1582],{"className":527,"code":1581,"language":530,"meta":531,"style":531},"import { DeliveryStatus } from \"@heximon\u002Fnotifications\";\n\nconst result = await welcomeEmail.sendTo({ name: \"Ada\", email: \"ada@example.com\" });\n\nif (result.status === DeliveryStatus.Accepted) {\n  console.log(\"sent:\", result.messageId);\n} else {\n  console.error(\"failed:\", result.reason, \"retryable:\", result.retryable);\n}\n",[503,1583,1584,1605,1609,1665,1669,1694,1727,1736,1785],{"__ignoreMap":531},[535,1585,1586,1588,1590,1593,1595,1597,1599,1601,1603],{"class":537,"line":538},[535,1587,542],{"class":541},[535,1589,546],{"class":545},[535,1591,1592],{"class":549}," DeliveryStatus",[535,1594,553],{"class":545},[535,1596,556],{"class":541},[535,1598,559],{"class":545},[535,1600,591],{"class":562},[535,1602,566],{"class":545},[535,1604,569],{"class":545},[535,1606,1607],{"class":537,"line":572},[535,1608,633],{"emptyLinePlaceholder":632},[535,1610,1611,1614,1617,1619,1622,1625,1627,1630,1632,1635,1637,1639,1641,1644,1646,1648,1650,1652,1654,1657,1659,1661,1663],{"class":537,"line":598},[535,1612,1613],{"class":649},"const",[535,1615,1616],{"class":549}," result ",[535,1618,715],{"class":545},[535,1620,1621],{"class":541}," await",[535,1623,1624],{"class":549}," welcomeEmail",[535,1626,860],{"class":545},[535,1628,1629],{"class":863},"sendTo",[535,1631,819],{"class":549},[535,1633,1634],{"class":545},"{",[535,1636,1247],{"class":674},[535,1638,678],{"class":545},[535,1640,559],{"class":545},[535,1642,1643],{"class":562},"Ada",[535,1645,566],{"class":545},[535,1647,610],{"class":545},[535,1649,1256],{"class":674},[535,1651,678],{"class":545},[535,1653,559],{"class":545},[535,1655,1656],{"class":562},"ada@example.com",[535,1658,566],{"class":545},[535,1660,553],{"class":545},[535,1662,944],{"class":549},[535,1664,569],{"class":545},[535,1666,1667],{"class":537,"line":629},[535,1668,633],{"emptyLinePlaceholder":632},[535,1670,1671,1674,1677,1679,1682,1685,1687,1689,1692],{"class":537,"line":636},[535,1672,1673],{"class":541},"if",[535,1675,1676],{"class":549}," (result",[535,1678,860],{"class":545},[535,1680,1681],{"class":549},"status ",[535,1683,1684],{"class":545},"===",[535,1686,1592],{"class":549},[535,1688,860],{"class":545},[535,1690,1691],{"class":549},"Accepted) ",[535,1693,770],{"class":545},[535,1695,1696,1699,1701,1704,1706,1708,1711,1713,1715,1718,1720,1723,1725],{"class":537,"line":643},[535,1697,1698],{"class":549},"  console",[535,1700,860],{"class":545},[535,1702,1703],{"class":863},"log",[535,1705,819],{"class":674},[535,1707,566],{"class":545},[535,1709,1710],{"class":562},"sent:",[535,1712,566],{"class":545},[535,1714,610],{"class":545},[535,1716,1717],{"class":549}," result",[535,1719,860],{"class":545},[535,1721,1722],{"class":549},"messageId",[535,1724,944],{"class":674},[535,1726,569],{"class":545},[535,1728,1729,1731,1734],{"class":537,"line":665},[535,1730,1087],{"class":545},[535,1732,1733],{"class":541}," else",[535,1735,662],{"class":545},[535,1737,1738,1740,1742,1745,1747,1749,1752,1754,1756,1758,1760,1763,1765,1767,1770,1772,1774,1776,1778,1781,1783],{"class":537,"line":723},[535,1739,1698],{"class":549},[535,1741,860],{"class":545},[535,1743,1744],{"class":863},"error",[535,1746,819],{"class":674},[535,1748,566],{"class":545},[535,1750,1751],{"class":562},"failed:",[535,1753,566],{"class":545},[535,1755,610],{"class":545},[535,1757,1717],{"class":549},[535,1759,860],{"class":545},[535,1761,1762],{"class":549},"reason",[535,1764,610],{"class":545},[535,1766,559],{"class":545},[535,1768,1769],{"class":562},"retryable:",[535,1771,566],{"class":545},[535,1773,610],{"class":545},[535,1775,1717],{"class":549},[535,1777,860],{"class":545},[535,1779,1780],{"class":549},"retryable",[535,1782,944],{"class":674},[535,1784,569],{"class":545},[535,1786,1787],{"class":537,"line":728},[535,1788,990],{"class":545},[515,1790,1792],{"id":1791},"go-to-production","Go to production",[499,1794,1795,1796,1799,1800,1803,1804,1806,1807,1810],{},"Swap the binding, not the code. ",[503,1797,1798],{},"resend"," is an optional peer — install it, then bind ",[503,1801,1802],{},"ResendTransport","\nto the ",[503,1805,505],{}," token via ",[503,1808,1809],{},"useFactory"," (it needs an API key, a literal value DI can't supply):",[525,1812,1816],{"className":1813,"code":1814,"language":1815,"meta":531,"style":531},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @heximon\u002Fnotifications resend\n","bash",[503,1817,1818],{"__ignoreMap":531},[535,1819,1820,1823,1826,1829],{"class":537,"line":538},[535,1821,1822],{"class":653},"pnpm",[535,1824,1825],{"class":562}," add",[535,1827,1828],{"class":562}," @heximon\u002Fnotifications",[535,1830,1831],{"class":562}," resend\n",[525,1833,1835],{"className":527,"code":1834,"filename":994,"language":530,"meta":531,"style":531},"import { Module } from \"@heximon\u002Fruntime\";\nimport { EmailTransport } from \"@heximon\u002Fnotifications\u002Femail\";\nimport { ResendTransport } from \"@heximon\u002Fnotifications\u002Femail\u002Fresend\";\nimport { WelcomeEmailService } from \".\u002Fwelcome-email.service\";\n\nexport class MailModule extends Module({\n  providers: [\n    { provide: EmailTransport, useFactory: () => new ResendTransport({ apiKey: process.env.RESEND_API_KEY! }) },\n    WelcomeEmailService,\n  ],\n  exports: [EmailTransport, WelcomeEmailService],\n}) {}\n",[503,1836,1837,1857,1877,1899,1919,1923,1939,1948,2009,2016,2023,2038],{"__ignoreMap":531},[535,1838,1839,1841,1843,1845,1847,1849,1851,1853,1855],{"class":537,"line":538},[535,1840,542],{"class":541},[535,1842,546],{"class":545},[535,1844,1005],{"class":549},[535,1846,553],{"class":545},[535,1848,556],{"class":541},[535,1850,559],{"class":545},[535,1852,563],{"class":562},[535,1854,566],{"class":545},[535,1856,569],{"class":545},[535,1858,1859,1861,1863,1865,1867,1869,1871,1873,1875],{"class":537,"line":572},[535,1860,542],{"class":541},[535,1862,546],{"class":545},[535,1864,613],{"class":549},[535,1866,553],{"class":545},[535,1868,556],{"class":541},[535,1870,559],{"class":545},[535,1872,622],{"class":562},[535,1874,566],{"class":545},[535,1876,569],{"class":545},[535,1878,1879,1881,1883,1886,1888,1890,1892,1895,1897],{"class":537,"line":598},[535,1880,542],{"class":541},[535,1882,546],{"class":545},[535,1884,1885],{"class":549}," ResendTransport",[535,1887,553],{"class":545},[535,1889,556],{"class":541},[535,1891,559],{"class":545},[535,1893,1894],{"class":562},"@heximon\u002Fnotifications\u002Femail\u002Fresend",[535,1896,566],{"class":545},[535,1898,569],{"class":545},[535,1900,1901,1903,1905,1907,1909,1911,1913,1915,1917],{"class":537,"line":629},[535,1902,542],{"class":541},[535,1904,546],{"class":545},[535,1906,1194],{"class":549},[535,1908,553],{"class":545},[535,1910,556],{"class":541},[535,1912,559],{"class":545},[535,1914,1504],{"class":562},[535,1916,566],{"class":545},[535,1918,569],{"class":545},[535,1920,1921],{"class":537,"line":636},[535,1922,633],{"emptyLinePlaceholder":632},[535,1924,1925,1927,1929,1931,1933,1935,1937],{"class":537,"line":643},[535,1926,646],{"class":541},[535,1928,650],{"class":649},[535,1930,1051],{"class":653},[535,1932,657],{"class":649},[535,1934,1005],{"class":863},[535,1936,819],{"class":549},[535,1938,770],{"class":545},[535,1940,1941,1943,1945],{"class":537,"line":665},[535,1942,1064],{"class":674},[535,1944,678],{"class":545},[535,1946,1947],{"class":549}," [\n",[535,1949,1950,1953,1956,1958,1960,1962,1965,1967,1970,1973,1975,1977,1979,1981,1984,1986,1989,1991,1994,1996,1999,2002,2004,2006],{"class":537,"line":723},[535,1951,1952],{"class":545},"    {",[535,1954,1955],{"class":674}," provide",[535,1957,678],{"class":545},[535,1959,613],{"class":549},[535,1961,610],{"class":545},[535,1963,1964],{"class":863}," useFactory",[535,1966,678],{"class":545},[535,1968,1969],{"class":545}," ()",[535,1971,1972],{"class":649}," =>",[535,1974,970],{"class":545},[535,1976,1885],{"class":863},[535,1978,819],{"class":549},[535,1980,1634],{"class":545},[535,1982,1983],{"class":674}," apiKey",[535,1985,678],{"class":545},[535,1987,1988],{"class":549}," process",[535,1990,860],{"class":545},[535,1992,1993],{"class":549},"env",[535,1995,860],{"class":545},[535,1997,1998],{"class":549},"RESEND_API_KEY",[535,2000,2001],{"class":545},"!",[535,2003,553],{"class":545},[535,2005,1090],{"class":549},[535,2007,2008],{"class":545},"},\n",[535,2010,2011,2014],{"class":537,"line":728},[535,2012,2013],{"class":549},"    WelcomeEmailService",[535,2015,895],{"class":545},[535,2017,2018,2021],{"class":537,"line":773},[535,2019,2020],{"class":549},"  ]",[535,2022,895],{"class":545},[535,2024,2025,2027,2029,2032,2034,2036],{"class":537,"line":793},[535,2026,1076],{"class":674},[535,2028,678],{"class":545},[535,2030,2031],{"class":549}," [EmailTransport",[535,2033,610],{"class":545},[535,2035,1542],{"class":549},[535,2037,895],{"class":545},[535,2039,2040,2042,2044],{"class":537,"line":799},[535,2041,1087],{"class":545},[535,2043,1090],{"class":549},[535,2045,1093],{"class":545},[499,2047,2048,2051,2052,2054,2055,2058],{},[503,2049,2050],{},"WelcomeEmailService"," is unchanged — it injects the abstract ",[503,2053,505],{},", never a concrete\nprovider, so this one binding is the entire production cutover. ",[503,2056,2057],{},"unemail"," is the other optional-peer\ntransport if you'd rather not depend on Resend.",[2060,2061,2063,2070],"callout",{"type":2062},"info",[2064,2065,2066,2067,2069],"strong",{},"One ",[503,2068,505],{}," per app graph."," The token resolves to exactly one subclass — binding two\nfails the build with an ambiguous-binding error. Compose per environment (dev binds the capture\ntransport, production binds Resend) rather than binding both at once.",[515,2072,2074],{"id":2073},"see-also","See also",[2076,2077,2078,2088,2097],"ul",{},[2079,2080,2081,2083,2084,2087],"li",{},[511,2082,180],{"href":181}," — the full surface: SMS, push, per-channel\ntemplating, suppression on bounces, and the ",[503,2085,2086],{},"NotificationDispatcher"," for fanning one logical\nnotification across channels.",[2079,2089,2090,2092,2093,2096],{},[511,2091,461],{"href":462}," — send from an ",[503,2094,2095],{},"IntegrationEventHandler"," instead of\ninline, so the triggering request never waits on provider latency.",[2079,2098,2099,2107,2108,2111],{},[511,2100,2104],{"href":2101,"rel":2102},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fgap\u002Fnotifications",[2103],"nofollow",[2064,2105,2106],{},"the gap's notifications example","\n— email + SMS + push over capture transports, a ",[503,2109,2110],{},"SuppressionService","-backed webhook controller, and an\nin-process test asserting exactly what each channel sent.",[2113,2114,2115],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":531,"searchDepth":572,"depth":572,"links":2117},[2118,2119,2120,2121,2122],{"id":517,"depth":572,"text":518},{"id":1110,"depth":572,"text":1111},{"id":1569,"depth":572,"text":1570},{"id":1791,"depth":572,"text":1792},{"id":2073,"depth":572,"text":2074},"Inject the abstract EmailTransport DI token, compose an EmailMessage with Mustache placeholders, and send it — with a capture transport for dev\u002Ftests and ResendTransport as the one-line production swap.","md",null,{},{"icon":430},{"title":427,"description":2123},"U5NgzlOt_j7smBpH0FzxuH-cUfMGU2l4c44sbqYJstE",[2131,2133],{"title":416,"path":417,"stem":418,"description":2132,"icon":419,"children":-1},"The nominal-satisfier walk (a concrete subclass satisfies an abstract token, AmbiguousProviders on a tie), array-provide, useExisting aliasing, useFactory with multiple dependencies, the generic-token pitfall (DrizzleLibSQLConfig and named subclasses), useValue with eager, factory-form Module((config) => ({...})) configs, and overriding the ErrorEnvelope.",{"title":432,"path":433,"stem":434,"description":2134,"icon":435,"children":-1},"Accept a multipart\u002Fform-data upload validated by Route.files(), read it with readValidatedFormData(), and stream it into the abstract BlobStore DI token — swappable for filesystem, S3, or R2 with no controller change.",1783300761903]