[{"data":1,"prerenderedAt":5665},["ShallowReactive",2],{"navigation_docs":3,"-docs-capabilities-notifications":493,"-docs-capabilities-notifications-surround":5660},[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":180,"body":495,"description":5653,"extension":5654,"links":5655,"meta":5656,"navigation":5657,"path":181,"seo":5658,"stem":182,"__hash__":5659},"docs\u002Fdocs\u002F03.capabilities\u002F09.notifications.md",{"type":496,"value":497,"toc":5639},"minimark",[498,511,527,532,542,606,609,613,630,910,922,1088,1092,1106,1133,1330,1336,1400,1462,1466,1478,1674,1677,2004,2013,2017,2035,2418,2424,2519,2539,2543,2549,2897,2908,2912,2951,3190,3204,3236,3267,3274,3761,3778,3782,3816,4102,4106,4140,4157,4177,4918,4924,5202,5206,5223,5237,5514,5518,5521,5568,5575,5579,5635],[499,500,501,502,506,507,510],"p",{},"Send an email, a text, or a push notification through a transport you can swap without touching the code\nthat composes it. Every channel narrows the same two shapes — a templated ",[503,504,505],"code",{},"Message"," and the ",[503,508,509],{},"MessageTransport","\nthat delivers it — so a capture transport in a test and Resend or Twilio in production run the exact same\nsending code.",[499,512,513,514,517,518,521,522,526],{},"Start with email: you build an ",[503,515,516],{},"EmailMessage"," (an HTML body with Mustache placeholders) and\nhand it to an injected ",[503,519,520],{},"EmailTransport",". For the shortest path — one transport, one send — see the\n",[523,524,525],"a",{"href":428},"send-an-email recipe","; this page covers the full surface: composing,\nsending, custom transports, the other channels, templating, and orchestration.",[528,529,531],"h2",{"id":530},"install","Install",[499,533,534,537,538,541],{},[503,535,536],{},"resend"," and ",[503,539,540],{},"unemail"," are optional peers — install only the one whose transport you bind. It loads on\nfirst send, so the email layer never pulls in a provider you don't use.",[543,544,545,571,589],"code-group",{},[546,547,553],"pre",{"className":548,"code":549,"filename":550,"language":551,"meta":552,"style":552},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @heximon\u002Fnotifications\n","core only","bash","",[503,554,555],{"__ignoreMap":552},[556,557,560,564,568],"span",{"class":558,"line":559},"line",1,[556,561,563],{"class":562},"sBMFI","pnpm",[556,565,567],{"class":566},"sfazB"," add",[556,569,570],{"class":566}," @heximon\u002Fnotifications\n",[546,572,575],{"className":548,"code":573,"filename":574,"language":551,"meta":552,"style":552},"pnpm add @heximon\u002Fnotifications resend\n","with Resend",[503,576,577],{"__ignoreMap":552},[556,578,579,581,583,586],{"class":558,"line":559},[556,580,563],{"class":562},[556,582,567],{"class":566},[556,584,585],{"class":566}," @heximon\u002Fnotifications",[556,587,588],{"class":566}," resend\n",[546,590,593],{"className":548,"code":591,"filename":592,"language":551,"meta":552,"style":552},"pnpm add @heximon\u002Fnotifications unemail\n","with unemail",[503,594,595],{"__ignoreMap":552},[556,596,597,599,601,603],{"class":558,"line":559},[556,598,563],{"class":562},[556,600,567],{"class":566},[556,602,585],{"class":566},[556,604,605],{"class":566}," unemail\n",[499,607,608],{},"There's no compiler plugin to register — the abstract transports are plain DI tokens a module binds.",[528,610,612],{"id":611},"compose-an-email","Compose an email",[499,614,615,617,618,621,622,625,626,629],{},[503,616,516],{}," is built from a single props object. The HTML ",[503,619,620],{},"content"," carries Mustache placeholders,\n",[503,623,624],{},"placeholders"," supplies their values, and ",[503,627,628],{},"getContent()"," renders them.",[546,631,635],{"className":632,"code":633,"language":634,"meta":552,"style":552},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { EmailMessage } from \"@heximon\u002Fnotifications\u002Femail\";\n\nconst message = new EmailMessage({\n  from: { name: \"Heximon Example\", email: \"noreply@example.com\" },\n  to: { name: \"Jane Doe\", email: \"jane@example.com\" },\n  subject: \"Welcome aboard!\",\n  content: \"\u003Cp>Hi {{ name }}, your account id is \u003Ccode>{{ userId }}\u003C\u002Fcode>.\u003C\u002Fp>\",\n  placeholders: { name: \"Jane\", userId: \"01HF…\" },\n});\n\nmessage.getContent();           \u002F\u002F rendered HTML, Mustache applied\nawait message.getTextContent(); \u002F\u002F plain-text alternative derived from the HTML\n","ts",[503,636,637,669,676,701,743,779,797,814,851,862,867,889],{"__ignoreMap":552},[556,638,639,643,647,651,654,657,660,663,666],{"class":558,"line":559},[556,640,642],{"class":641},"s7zQu","import",[556,644,646],{"class":645},"sMK4o"," {",[556,648,650],{"class":649},"sTEyZ"," EmailMessage",[556,652,653],{"class":645}," }",[556,655,656],{"class":641}," from",[556,658,659],{"class":645}," \"",[556,661,662],{"class":566},"@heximon\u002Fnotifications\u002Femail",[556,664,665],{"class":645},"\"",[556,667,668],{"class":645},";\n",[556,670,672],{"class":558,"line":671},2,[556,673,675],{"emptyLinePlaceholder":674},true,"\n",[556,677,679,683,686,689,692,695,698],{"class":558,"line":678},3,[556,680,682],{"class":681},"spNyl","const",[556,684,685],{"class":649}," message ",[556,687,688],{"class":645},"=",[556,690,691],{"class":645}," new",[556,693,650],{"class":694},"s2Zo4",[556,696,697],{"class":649},"(",[556,699,700],{"class":645},"{\n",[556,702,704,708,711,713,716,718,720,723,725,728,731,733,735,738,740],{"class":558,"line":703},4,[556,705,707],{"class":706},"swJcz","  from",[556,709,710],{"class":645},":",[556,712,646],{"class":645},[556,714,715],{"class":706}," name",[556,717,710],{"class":645},[556,719,659],{"class":645},[556,721,722],{"class":566},"Heximon Example",[556,724,665],{"class":645},[556,726,727],{"class":645},",",[556,729,730],{"class":706}," email",[556,732,710],{"class":645},[556,734,659],{"class":645},[556,736,737],{"class":566},"noreply@example.com",[556,739,665],{"class":645},[556,741,742],{"class":645}," },\n",[556,744,746,749,751,753,755,757,759,762,764,766,768,770,772,775,777],{"class":558,"line":745},5,[556,747,748],{"class":706},"  to",[556,750,710],{"class":645},[556,752,646],{"class":645},[556,754,715],{"class":706},[556,756,710],{"class":645},[556,758,659],{"class":645},[556,760,761],{"class":566},"Jane Doe",[556,763,665],{"class":645},[556,765,727],{"class":645},[556,767,730],{"class":706},[556,769,710],{"class":645},[556,771,659],{"class":645},[556,773,774],{"class":566},"jane@example.com",[556,776,665],{"class":645},[556,778,742],{"class":645},[556,780,782,785,787,789,792,794],{"class":558,"line":781},6,[556,783,784],{"class":706},"  subject",[556,786,710],{"class":645},[556,788,659],{"class":645},[556,790,791],{"class":566},"Welcome aboard!",[556,793,665],{"class":645},[556,795,796],{"class":645},",\n",[556,798,800,803,805,807,810,812],{"class":558,"line":799},7,[556,801,802],{"class":706},"  content",[556,804,710],{"class":645},[556,806,659],{"class":645},[556,808,809],{"class":566},"\u003Cp>Hi {{ name }}, your account id is \u003Ccode>{{ userId }}\u003C\u002Fcode>.\u003C\u002Fp>",[556,811,665],{"class":645},[556,813,796],{"class":645},[556,815,817,820,822,824,826,828,830,833,835,837,840,842,844,847,849],{"class":558,"line":816},8,[556,818,819],{"class":706},"  placeholders",[556,821,710],{"class":645},[556,823,646],{"class":645},[556,825,715],{"class":706},[556,827,710],{"class":645},[556,829,659],{"class":645},[556,831,832],{"class":566},"Jane",[556,834,665],{"class":645},[556,836,727],{"class":645},[556,838,839],{"class":706}," userId",[556,841,710],{"class":645},[556,843,659],{"class":645},[556,845,846],{"class":566},"01HF…",[556,848,665],{"class":645},[556,850,742],{"class":645},[556,852,854,857,860],{"class":558,"line":853},9,[556,855,856],{"class":645},"}",[556,858,859],{"class":649},")",[556,861,668],{"class":645},[556,863,865],{"class":558,"line":864},10,[556,866,675],{"emptyLinePlaceholder":674},[556,868,870,873,876,879,882,885],{"class":558,"line":869},11,[556,871,872],{"class":649},"message",[556,874,875],{"class":645},".",[556,877,878],{"class":694},"getContent",[556,880,881],{"class":649},"()",[556,883,884],{"class":645},";",[556,886,888],{"class":887},"sHwdD","           \u002F\u002F rendered HTML, Mustache applied\n",[556,890,892,895,898,900,903,905,907],{"class":558,"line":891},12,[556,893,894],{"class":641},"await",[556,896,897],{"class":649}," message",[556,899,875],{"class":645},[556,901,902],{"class":694},"getTextContent",[556,904,881],{"class":649},[556,906,884],{"class":645},[556,908,909],{"class":887}," \u002F\u002F plain-text alternative derived from the HTML\n",[499,911,912,913,917,918,921],{},"A placeholder value can be static or a ",[914,915,916],"strong",{},"thunk"," — ",[503,919,920],{},"placeholders: { token: () => mintToken() }"," —\nevaluated only when the body renders, so an expensive or single-use value is computed lazily.",[923,924,925,942],"table",{},[926,927,928],"thead",{},[929,930,931,936,939],"tr",{},[932,933,935],"th",{"align":934},"left","Prop",[932,937,938],{"align":934},"Type",[932,940,941],{"align":934},"Notes",[943,944,945,963,978,993,1006,1020,1038,1052,1067],"tbody",{},[929,946,947,953,958],{},[948,949,950],"td",{"align":934},[503,951,952],{},"from",[948,954,955],{"align":934},[503,956,957],{},"EmailAddress",[948,959,960],{"align":934},[503,961,962],{},"{ name?, email }",[929,964,965,970,975],{},[948,966,967],{"align":934},[503,968,969],{},"to",[948,971,972],{"align":934},[503,973,974],{},"EmailAddress | EmailAddress[]",[948,976,977],{"align":934},"Recipient(s)",[929,979,980,985,990],{},[948,981,982],{"align":934},[503,983,984],{},"subject",[948,986,987],{"align":934},[503,988,989],{},"string",[948,991,992],{"align":934},"Subject line (not templated)",[929,994,995,999,1003],{},[948,996,997],{"align":934},[503,998,620],{},[948,1000,1001],{"align":934},[503,1002,989],{},[948,1004,1005],{"align":934},"HTML body, Mustache-enabled",[929,1007,1008,1012,1017],{},[948,1009,1010],{"align":934},[503,1011,624],{},[948,1013,1014],{"align":934},[503,1015,1016],{},"Placeholders",[948,1018,1019],{"align":934},"Static values or thunks",[929,1021,1022,1031,1035],{},[948,1023,1024,1027,1028],{"align":934},[503,1025,1026],{},"cc"," \u002F ",[503,1029,1030],{},"bcc",[948,1032,1033],{"align":934},[503,1034,974],{},[948,1036,1037],{"align":934},"Optional",[929,1039,1040,1045,1049],{},[948,1041,1042],{"align":934},[503,1043,1044],{},"replyTo",[948,1046,1047],{"align":934},[503,1048,957],{},[948,1050,1051],{"align":934},"Optional Reply-To",[929,1053,1054,1059,1064],{},[948,1055,1056],{"align":934},[503,1057,1058],{},"headers",[948,1060,1061],{"align":934},[503,1062,1063],{},"Record\u003Cstring, string>",[948,1065,1066],{"align":934},"Custom headers",[929,1068,1069,1074,1079],{},[948,1070,1071],{"align":934},[503,1072,1073],{},"attachments",[948,1075,1076],{"align":934},[503,1077,1078],{},"EmailAttachment[]",[948,1080,1081,1083,1084,1087],{"align":934},[503,1082,989],{}," content is Base64; a ",[503,1085,1086],{},"Buffer"," is encoded for you",[528,1089,1091],{"id":1090},"send-and-read-the-result","Send and read the result",[499,1093,1094,1097,1098,1101,1102,1105],{},[503,1095,1096],{},"send()"," ",[914,1099,1100],{},"never throws"," — it drives the lifecycle and returns a ",[503,1103,1104],{},"DeliveryResult",", recording a failure on\nthe message rather than raising it, so a missed email can't take down the request that triggered it.",[499,1107,1108,1109,1112,1113,1116,1117,1120,1121,1124,1125,1128,1129,1132],{},"The result carries the outcome explicitly: a ",[503,1110,1111],{},"status"," (",[503,1114,1115],{},"Accepted"," or ",[503,1118,1119],{},"Failed","), the delivery ",[503,1122,1123],{},"channel",", the\nprovider ",[503,1126,1127],{},"messageId"," on acceptance, and — on failure — whether it is ",[503,1130,1131],{},"retryable"," (so a queue consumer knows\nto re-try a rate limit but dead-letter an invalid address):",[546,1134,1136],{"className":632,"code":1135,"language":634,"meta":552,"style":552},"import { DeliveryStatus } from \"@heximon\u002Fnotifications\";\n\nconst result = await transport.send(message);\n\nif (result.status === DeliveryStatus.Accepted) {\n  console.log(\"accepted:\", result.channel, result.messageId, result.timestamp);\n} else {\n  console.error(\"failed:\", result.reason, \"retryable:\", result.retryable);\n}\n",[503,1137,1138,1160,1164,1189,1193,1218,1267,1277,1325],{"__ignoreMap":552},[556,1139,1140,1142,1144,1147,1149,1151,1153,1156,1158],{"class":558,"line":559},[556,1141,642],{"class":641},[556,1143,646],{"class":645},[556,1145,1146],{"class":649}," DeliveryStatus",[556,1148,653],{"class":645},[556,1150,656],{"class":641},[556,1152,659],{"class":645},[556,1154,1155],{"class":566},"@heximon\u002Fnotifications",[556,1157,665],{"class":645},[556,1159,668],{"class":645},[556,1161,1162],{"class":558,"line":671},[556,1163,675],{"emptyLinePlaceholder":674},[556,1165,1166,1168,1171,1173,1176,1179,1181,1184,1187],{"class":558,"line":678},[556,1167,682],{"class":681},[556,1169,1170],{"class":649}," result ",[556,1172,688],{"class":645},[556,1174,1175],{"class":641}," await",[556,1177,1178],{"class":649}," transport",[556,1180,875],{"class":645},[556,1182,1183],{"class":694},"send",[556,1185,1186],{"class":649},"(message)",[556,1188,668],{"class":645},[556,1190,1191],{"class":558,"line":703},[556,1192,675],{"emptyLinePlaceholder":674},[556,1194,1195,1198,1201,1203,1206,1209,1211,1213,1216],{"class":558,"line":745},[556,1196,1197],{"class":641},"if",[556,1199,1200],{"class":649}," (result",[556,1202,875],{"class":645},[556,1204,1205],{"class":649},"status ",[556,1207,1208],{"class":645},"===",[556,1210,1146],{"class":649},[556,1212,875],{"class":645},[556,1214,1215],{"class":649},"Accepted) ",[556,1217,700],{"class":645},[556,1219,1220,1223,1225,1228,1230,1232,1235,1237,1239,1242,1244,1246,1248,1250,1252,1254,1256,1258,1260,1263,1265],{"class":558,"line":781},[556,1221,1222],{"class":649},"  console",[556,1224,875],{"class":645},[556,1226,1227],{"class":694},"log",[556,1229,697],{"class":706},[556,1231,665],{"class":645},[556,1233,1234],{"class":566},"accepted:",[556,1236,665],{"class":645},[556,1238,727],{"class":645},[556,1240,1241],{"class":649}," result",[556,1243,875],{"class":645},[556,1245,1123],{"class":649},[556,1247,727],{"class":645},[556,1249,1241],{"class":649},[556,1251,875],{"class":645},[556,1253,1127],{"class":649},[556,1255,727],{"class":645},[556,1257,1241],{"class":649},[556,1259,875],{"class":645},[556,1261,1262],{"class":649},"timestamp",[556,1264,859],{"class":706},[556,1266,668],{"class":645},[556,1268,1269,1271,1274],{"class":558,"line":799},[556,1270,856],{"class":645},[556,1272,1273],{"class":641}," else",[556,1275,1276],{"class":645}," {\n",[556,1278,1279,1281,1283,1286,1288,1290,1293,1295,1297,1299,1301,1304,1306,1308,1311,1313,1315,1317,1319,1321,1323],{"class":558,"line":816},[556,1280,1222],{"class":649},[556,1282,875],{"class":645},[556,1284,1285],{"class":694},"error",[556,1287,697],{"class":706},[556,1289,665],{"class":645},[556,1291,1292],{"class":566},"failed:",[556,1294,665],{"class":645},[556,1296,727],{"class":645},[556,1298,1241],{"class":649},[556,1300,875],{"class":645},[556,1302,1303],{"class":649},"reason",[556,1305,727],{"class":645},[556,1307,659],{"class":645},[556,1309,1310],{"class":566},"retryable:",[556,1312,665],{"class":645},[556,1314,727],{"class":645},[556,1316,1241],{"class":649},[556,1318,875],{"class":645},[556,1320,1131],{"class":649},[556,1322,859],{"class":706},[556,1324,668],{"class":645},[556,1326,1327],{"class":558,"line":853},[556,1328,1329],{"class":645},"}\n",[499,1331,1332,1333,710],{},"The message tracks its own state, readable any time with ",[503,1334,1335],{},"getStatus()",[923,1337,1338,1348],{},[926,1339,1340],{},[929,1341,1342,1345],{},[932,1343,1344],{"align":934},"Status",[932,1346,1347],{"align":934},"Meaning",[943,1349,1350,1360,1370,1387],{},[929,1351,1352,1357],{},[948,1353,1354],{"align":934},[503,1355,1356],{},"Draft",[948,1358,1359],{"align":934},"Initial state after construction",[929,1361,1362,1367],{},[948,1363,1364],{"align":934},[503,1365,1366],{},"Pending",[948,1368,1369],{"align":934},"Delivery has started",[929,1371,1372,1377],{},[948,1373,1374],{"align":934},[503,1375,1376],{},"Sent",[948,1378,1379,1380,537,1383,1386],{"align":934},"The provider accepted it — ",[503,1381,1382],{},"getId()",[503,1384,1385],{},"getSentDate()"," are now set",[929,1388,1389,1393],{},[948,1390,1391],{"align":934},[503,1392,1119],{},[948,1394,1395,1396,1399],{"align":934},"Delivery failed — ",[503,1397,1398],{},"getErrors()"," is populated",[546,1401,1403],{"className":632,"code":1402,"language":634,"meta":552,"style":552},"import { MessageStatus } from \"@heximon\u002Fnotifications\";\n\nmessage.getStatus(); \u002F\u002F MessageStatus.Draft | .Pending | .Sent | .Failed\nmessage.isSent();    \u002F\u002F boolean\n",[503,1404,1405,1426,1430,1446],{"__ignoreMap":552},[556,1406,1407,1409,1411,1414,1416,1418,1420,1422,1424],{"class":558,"line":559},[556,1408,642],{"class":641},[556,1410,646],{"class":645},[556,1412,1413],{"class":649}," MessageStatus",[556,1415,653],{"class":645},[556,1417,656],{"class":641},[556,1419,659],{"class":645},[556,1421,1155],{"class":566},[556,1423,665],{"class":645},[556,1425,668],{"class":645},[556,1427,1428],{"class":558,"line":671},[556,1429,675],{"emptyLinePlaceholder":674},[556,1431,1432,1434,1436,1439,1441,1443],{"class":558,"line":678},[556,1433,872],{"class":649},[556,1435,875],{"class":645},[556,1437,1438],{"class":694},"getStatus",[556,1440,881],{"class":649},[556,1442,884],{"class":645},[556,1444,1445],{"class":887}," \u002F\u002F MessageStatus.Draft | .Pending | .Sent | .Failed\n",[556,1447,1448,1450,1452,1455,1457,1459],{"class":558,"line":703},[556,1449,872],{"class":649},[556,1451,875],{"class":645},[556,1453,1454],{"class":694},"isSent",[556,1456,881],{"class":649},[556,1458,884],{"class":645},[556,1460,1461],{"class":887},"    \u002F\u002F boolean\n",[528,1463,1465],{"id":1464},"bind-a-transport-in-a-module","Bind a transport in a module",[499,1467,1468,1470,1471,917,1474,1477],{},[503,1469,520],{}," is an abstract class, and an abstract class is a DI token. Bind the real provider\nthrough ",[503,1472,1473],{},"useFactory",[503,1475,1476],{},"ResendTransport"," takes a config object (the API key), a literal value DI can't\nsupply, so you build it yourself.",[546,1479,1482],{"className":632,"code":1480,"filename":1481,"language":634,"meta":552,"style":552},"import { Module } from \"@heximon\u002Fruntime\";\nimport { EmailTransport } from \"@heximon\u002Fnotifications\u002Femail\";\nimport { ResendTransport } from \"@heximon\u002Fnotifications\u002Femail\u002Fresend\";\n\nexport class MailModule extends Module({\n  providers: [\n    { provide: EmailTransport, useFactory: () => new ResendTransport({ apiKey: process.env.RESEND_API_KEY! }) },\n  ],\n  exports: [EmailTransport],\n}) {}\n","mail.module.ts",[503,1483,1484,1506,1527,1549,1553,1573,1583,1646,1653,1665],{"__ignoreMap":552},[556,1485,1486,1488,1490,1493,1495,1497,1499,1502,1504],{"class":558,"line":559},[556,1487,642],{"class":641},[556,1489,646],{"class":645},[556,1491,1492],{"class":649}," Module",[556,1494,653],{"class":645},[556,1496,656],{"class":641},[556,1498,659],{"class":645},[556,1500,1501],{"class":566},"@heximon\u002Fruntime",[556,1503,665],{"class":645},[556,1505,668],{"class":645},[556,1507,1508,1510,1512,1515,1517,1519,1521,1523,1525],{"class":558,"line":671},[556,1509,642],{"class":641},[556,1511,646],{"class":645},[556,1513,1514],{"class":649}," EmailTransport",[556,1516,653],{"class":645},[556,1518,656],{"class":641},[556,1520,659],{"class":645},[556,1522,662],{"class":566},[556,1524,665],{"class":645},[556,1526,668],{"class":645},[556,1528,1529,1531,1533,1536,1538,1540,1542,1545,1547],{"class":558,"line":678},[556,1530,642],{"class":641},[556,1532,646],{"class":645},[556,1534,1535],{"class":649}," ResendTransport",[556,1537,653],{"class":645},[556,1539,656],{"class":641},[556,1541,659],{"class":645},[556,1543,1544],{"class":566},"@heximon\u002Fnotifications\u002Femail\u002Fresend",[556,1546,665],{"class":645},[556,1548,668],{"class":645},[556,1550,1551],{"class":558,"line":703},[556,1552,675],{"emptyLinePlaceholder":674},[556,1554,1555,1558,1561,1564,1567,1569,1571],{"class":558,"line":745},[556,1556,1557],{"class":641},"export",[556,1559,1560],{"class":681}," class",[556,1562,1563],{"class":562}," MailModule",[556,1565,1566],{"class":681}," extends",[556,1568,1492],{"class":694},[556,1570,697],{"class":649},[556,1572,700],{"class":645},[556,1574,1575,1578,1580],{"class":558,"line":781},[556,1576,1577],{"class":706},"  providers",[556,1579,710],{"class":645},[556,1581,1582],{"class":649}," [\n",[556,1584,1585,1588,1591,1593,1595,1597,1600,1602,1605,1608,1610,1612,1614,1617,1620,1622,1625,1627,1630,1632,1635,1638,1640,1643],{"class":558,"line":799},[556,1586,1587],{"class":645},"    {",[556,1589,1590],{"class":706}," provide",[556,1592,710],{"class":645},[556,1594,1514],{"class":649},[556,1596,727],{"class":645},[556,1598,1599],{"class":694}," useFactory",[556,1601,710],{"class":645},[556,1603,1604],{"class":645}," ()",[556,1606,1607],{"class":681}," =>",[556,1609,691],{"class":645},[556,1611,1535],{"class":694},[556,1613,697],{"class":649},[556,1615,1616],{"class":645},"{",[556,1618,1619],{"class":706}," apiKey",[556,1621,710],{"class":645},[556,1623,1624],{"class":649}," process",[556,1626,875],{"class":645},[556,1628,1629],{"class":649},"env",[556,1631,875],{"class":645},[556,1633,1634],{"class":649},"RESEND_API_KEY",[556,1636,1637],{"class":645},"!",[556,1639,653],{"class":645},[556,1641,1642],{"class":649},") ",[556,1644,1645],{"class":645},"},\n",[556,1647,1648,1651],{"class":558,"line":816},[556,1649,1650],{"class":649},"  ]",[556,1652,796],{"class":645},[556,1654,1655,1658,1660,1663],{"class":558,"line":853},[556,1656,1657],{"class":706},"  exports",[556,1659,710],{"class":645},[556,1661,1662],{"class":649}," [EmailTransport]",[556,1664,796],{"class":645},[556,1666,1667,1669,1671],{"class":558,"line":864},[556,1668,856],{"class":645},[556,1670,1642],{"class":649},[556,1672,1673],{"class":645},"{}\n",[499,1675,1676],{},"Consumers inject the abstract token and stay oblivious to the provider — switch it by changing this one\nbinding:",[546,1678,1681],{"className":632,"code":1679,"filename":1680,"language":634,"meta":552,"style":552},"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","welcome-email.service.ts",[503,1682,1683,1707,1731,1735,1746,1775,1779,1829,1847,1869,1905,1921,1937,1959,1969,1993,1999],{"__ignoreMap":552},[556,1684,1685,1687,1690,1692,1695,1697,1699,1701,1703,1705],{"class":558,"line":559},[556,1686,642],{"class":641},[556,1688,1689],{"class":641}," type",[556,1691,646],{"class":645},[556,1693,1694],{"class":649}," DeliveryResult",[556,1696,653],{"class":645},[556,1698,656],{"class":641},[556,1700,659],{"class":645},[556,1702,1155],{"class":566},[556,1704,665],{"class":645},[556,1706,668],{"class":645},[556,1708,1709,1711,1713,1715,1717,1719,1721,1723,1725,1727,1729],{"class":558,"line":671},[556,1710,642],{"class":641},[556,1712,646],{"class":645},[556,1714,650],{"class":649},[556,1716,727],{"class":645},[556,1718,1514],{"class":649},[556,1720,653],{"class":645},[556,1722,656],{"class":641},[556,1724,659],{"class":645},[556,1726,662],{"class":566},[556,1728,665],{"class":645},[556,1730,668],{"class":645},[556,1732,1733],{"class":558,"line":678},[556,1734,675],{"emptyLinePlaceholder":674},[556,1736,1737,1739,1741,1744],{"class":558,"line":703},[556,1738,1557],{"class":641},[556,1740,1560],{"class":681},[556,1742,1743],{"class":562}," WelcomeEmailService",[556,1745,1276],{"class":645},[556,1747,1748,1751,1754,1756,1759,1762,1766,1768,1770,1772],{"class":558,"line":745},[556,1749,1750],{"class":681},"  public",[556,1752,1753],{"class":681}," constructor",[556,1755,697],{"class":645},[556,1757,1758],{"class":681},"private",[556,1760,1761],{"class":681}," readonly",[556,1763,1765],{"class":1764},"sHdIc"," mail",[556,1767,710],{"class":645},[556,1769,1514],{"class":562},[556,1771,859],{"class":645},[556,1773,1774],{"class":645}," {}\n",[556,1776,1777],{"class":558,"line":781},[556,1778,675],{"emptyLinePlaceholder":674},[556,1780,1781,1783,1786,1789,1791,1794,1796,1798,1800,1802,1805,1807,1809,1811,1813,1816,1819,1822,1824,1827],{"class":558,"line":799},[556,1782,1750],{"class":681},[556,1784,1785],{"class":681}," async",[556,1787,1788],{"class":706}," sendTo",[556,1790,697],{"class":645},[556,1792,1793],{"class":1764},"user",[556,1795,710],{"class":645},[556,1797,646],{"class":645},[556,1799,715],{"class":706},[556,1801,710],{"class":645},[556,1803,1804],{"class":562}," string",[556,1806,884],{"class":645},[556,1808,730],{"class":706},[556,1810,710],{"class":645},[556,1812,1804],{"class":562},[556,1814,1815],{"class":645}," }):",[556,1817,1818],{"class":562}," Promise",[556,1820,1821],{"class":645},"\u003C",[556,1823,1104],{"class":562},[556,1825,1826],{"class":645},">",[556,1828,1276],{"class":645},[556,1830,1831,1834,1836,1839,1841,1843,1845],{"class":558,"line":816},[556,1832,1833],{"class":681},"    const",[556,1835,897],{"class":649},[556,1837,1838],{"class":645}," =",[556,1840,691],{"class":645},[556,1842,650],{"class":694},[556,1844,697],{"class":706},[556,1846,700],{"class":645},[556,1848,1849,1852,1854,1856,1858,1860,1862,1865,1867],{"class":558,"line":853},[556,1850,1851],{"class":706},"      from",[556,1853,710],{"class":645},[556,1855,646],{"class":645},[556,1857,730],{"class":706},[556,1859,710],{"class":645},[556,1861,659],{"class":645},[556,1863,1864],{"class":566},"hello@example.com",[556,1866,665],{"class":645},[556,1868,742],{"class":645},[556,1870,1871,1874,1876,1878,1880,1882,1885,1887,1890,1892,1894,1896,1898,1900,1903],{"class":558,"line":864},[556,1872,1873],{"class":706},"      to",[556,1875,710],{"class":645},[556,1877,646],{"class":645},[556,1879,715],{"class":706},[556,1881,710],{"class":645},[556,1883,1884],{"class":649}," user",[556,1886,875],{"class":645},[556,1888,1889],{"class":649},"name",[556,1891,727],{"class":645},[556,1893,730],{"class":706},[556,1895,710],{"class":645},[556,1897,1884],{"class":649},[556,1899,875],{"class":645},[556,1901,1902],{"class":649},"email",[556,1904,742],{"class":645},[556,1906,1907,1910,1912,1914,1917,1919],{"class":558,"line":869},[556,1908,1909],{"class":706},"      subject",[556,1911,710],{"class":645},[556,1913,659],{"class":645},[556,1915,1916],{"class":566},"Welcome, {{ name }}!",[556,1918,665],{"class":645},[556,1920,796],{"class":645},[556,1922,1923,1926,1928,1930,1933,1935],{"class":558,"line":891},[556,1924,1925],{"class":706},"      content",[556,1927,710],{"class":645},[556,1929,659],{"class":645},[556,1931,1932],{"class":566},"\u003Cp>Thanks for joining, {{ name }}.\u003C\u002Fp>",[556,1934,665],{"class":645},[556,1936,796],{"class":645},[556,1938,1940,1943,1945,1947,1949,1951,1953,1955,1957],{"class":558,"line":1939},13,[556,1941,1942],{"class":706},"      placeholders",[556,1944,710],{"class":645},[556,1946,646],{"class":645},[556,1948,715],{"class":706},[556,1950,710],{"class":645},[556,1952,1884],{"class":649},[556,1954,875],{"class":645},[556,1956,1889],{"class":649},[556,1958,742],{"class":645},[556,1960,1962,1965,1967],{"class":558,"line":1961},14,[556,1963,1964],{"class":645},"    }",[556,1966,859],{"class":706},[556,1968,668],{"class":645},[556,1970,1972,1975,1978,1981,1983,1985,1987,1989,1991],{"class":558,"line":1971},15,[556,1973,1974],{"class":641},"    return",[556,1976,1977],{"class":645}," this.",[556,1979,1980],{"class":649},"mail",[556,1982,875],{"class":645},[556,1984,1183],{"class":694},[556,1986,697],{"class":706},[556,1988,872],{"class":649},[556,1990,859],{"class":706},[556,1992,668],{"class":645},[556,1994,1996],{"class":558,"line":1995},16,[556,1997,1998],{"class":645},"  }\n",[556,2000,2002],{"class":558,"line":2001},17,[556,2003,1329],{"class":645},[499,2005,2006,2012],{},[914,2007,2008,2009,2011],{},"One ",[503,2010,520],{}," impl per app graph."," The token resolves to exactly one subclass, so binding two\nfails the build with an ambiguous-binding error — compose per environment, or inject a concrete subclass\nwhere you want a specific one.",[528,2014,2016],{"id":2015},"write-a-custom-transport","Write a custom transport",[499,2018,2019,2020,2022,2023,2026,2027,2030,2031,2034],{},"To target a provider with no adapter — or to capture mail in tests — extend ",[503,2021,520],{}," and\nimplement the protected ",[503,2024,2025],{},"transport()",". The base class owns every lifecycle transition, so your subclass\nis just the raw provider call: return a ",[503,2028,2029],{},"TransportResult"," on success, throw ",[503,2032,2033],{},"TransportError"," on failure.",[546,2036,2039],{"className":632,"code":2037,"filename":2038,"language":634,"meta":552,"style":552},"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","capture-email-transport.ts",[503,2040,2041,2062,2085,2111,2115,2120,2135,2186,2190,2232,2250,2254,2258,2291,2310,2326,2333,2347,2367,2376,2408,2413],{"__ignoreMap":552},[556,2042,2043,2045,2047,2050,2052,2054,2056,2058,2060],{"class":558,"line":559},[556,2044,642],{"class":641},[556,2046,646],{"class":645},[556,2048,2049],{"class":649}," uuid",[556,2051,653],{"class":645},[556,2053,656],{"class":641},[556,2055,659],{"class":645},[556,2057,1501],{"class":566},[556,2059,665],{"class":645},[556,2061,668],{"class":645},[556,2063,2064,2066,2068,2070,2073,2075,2077,2079,2081,2083],{"class":558,"line":671},[556,2065,642],{"class":641},[556,2067,1689],{"class":641},[556,2069,646],{"class":645},[556,2071,2072],{"class":649}," TransportResult",[556,2074,653],{"class":645},[556,2076,656],{"class":641},[556,2078,659],{"class":645},[556,2080,1155],{"class":566},[556,2082,665],{"class":645},[556,2084,668],{"class":645},[556,2086,2087,2089,2091,2093,2095,2097,2099,2101,2103,2105,2107,2109],{"class":558,"line":678},[556,2088,642],{"class":641},[556,2090,646],{"class":645},[556,2092,1689],{"class":641},[556,2094,650],{"class":649},[556,2096,727],{"class":645},[556,2098,1514],{"class":649},[556,2100,653],{"class":645},[556,2102,656],{"class":641},[556,2104,659],{"class":645},[556,2106,662],{"class":566},[556,2108,665],{"class":645},[556,2110,668],{"class":645},[556,2112,2113],{"class":558,"line":703},[556,2114,675],{"emptyLinePlaceholder":674},[556,2116,2117],{"class":558,"line":745},[556,2118,2119],{"class":887},"\u002F** Records every email in memory instead of delivering it. *\u002F\n",[556,2121,2122,2124,2126,2129,2131,2133],{"class":558,"line":781},[556,2123,1557],{"class":641},[556,2125,1560],{"class":681},[556,2127,2128],{"class":562}," CaptureEmailTransport",[556,2130,1566],{"class":681},[556,2132,1514],{"class":562},[556,2134,1276],{"class":645},[556,2136,2137,2140,2142,2145,2147,2149,2152,2154,2156,2158,2161,2163,2165,2167,2170,2172,2174,2176,2179,2181,2184],{"class":558,"line":799},[556,2138,2139],{"class":681},"  private",[556,2141,1761],{"class":681},[556,2143,2144],{"class":706}," captured",[556,2146,710],{"class":645},[556,2148,646],{"class":645},[556,2150,2151],{"class":706}," id",[556,2153,710],{"class":645},[556,2155,1804],{"class":562},[556,2157,884],{"class":645},[556,2159,2160],{"class":706}," subject",[556,2162,710],{"class":645},[556,2164,1804],{"class":562},[556,2166,884],{"class":645},[556,2168,2169],{"class":706}," content",[556,2171,710],{"class":645},[556,2173,1804],{"class":562},[556,2175,653],{"class":645},[556,2177,2178],{"class":649},"[] ",[556,2180,688],{"class":645},[556,2182,2183],{"class":649}," []",[556,2185,668],{"class":645},[556,2187,2188],{"class":558,"line":816},[556,2189,675],{"emptyLinePlaceholder":674},[556,2191,2192,2194,2197,2200,2202,2204,2206,2208,2210,2212,2214,2216,2218,2220,2222,2224,2226,2228,2230],{"class":558,"line":853},[556,2193,1750],{"class":681},[556,2195,2196],{"class":706}," sent",[556,2198,2199],{"class":645},"():",[556,2201,1761],{"class":681},[556,2203,646],{"class":645},[556,2205,2151],{"class":562},[556,2207,710],{"class":645},[556,2209,1804],{"class":649},[556,2211,884],{"class":645},[556,2213,2160],{"class":562},[556,2215,710],{"class":645},[556,2217,1804],{"class":649},[556,2219,884],{"class":645},[556,2221,2169],{"class":562},[556,2223,710],{"class":645},[556,2225,1804],{"class":649},[556,2227,653],{"class":645},[556,2229,2178],{"class":649},[556,2231,700],{"class":645},[556,2233,2234,2236,2239,2242,2245,2248],{"class":558,"line":864},[556,2235,1974],{"class":641},[556,2237,2238],{"class":706}," [",[556,2240,2241],{"class":645},"...this.",[556,2243,2244],{"class":649},"captured",[556,2246,2247],{"class":706},"]",[556,2249,668],{"class":645},[556,2251,2252],{"class":558,"line":869},[556,2253,1998],{"class":645},[556,2255,2256],{"class":558,"line":891},[556,2257,675],{"emptyLinePlaceholder":674},[556,2259,2260,2263,2266,2268,2270,2272,2274,2276,2278,2281,2283,2285,2287,2289],{"class":558,"line":1939},[556,2261,2262],{"class":681},"  protected",[556,2264,2265],{"class":681}," override",[556,2267,1785],{"class":681},[556,2269,1178],{"class":706},[556,2271,697],{"class":645},[556,2273,872],{"class":1764},[556,2275,710],{"class":645},[556,2277,650],{"class":562},[556,2279,2280],{"class":645},"):",[556,2282,1818],{"class":562},[556,2284,1821],{"class":645},[556,2286,2029],{"class":562},[556,2288,1826],{"class":645},[556,2290,1276],{"class":645},[556,2292,2293,2295,2297,2299,2301,2303,2306,2308],{"class":558,"line":1961},[556,2294,1833],{"class":681},[556,2296,2151],{"class":649},[556,2298,1838],{"class":645},[556,2300,2049],{"class":649},[556,2302,875],{"class":645},[556,2304,2305],{"class":694},"v7",[556,2307,881],{"class":706},[556,2309,668],{"class":645},[556,2311,2312,2315,2317,2319,2322,2324],{"class":558,"line":1971},[556,2313,2314],{"class":645},"    this.",[556,2316,2244],{"class":649},[556,2318,875],{"class":645},[556,2320,2321],{"class":694},"push",[556,2323,697],{"class":706},[556,2325,700],{"class":645},[556,2327,2328,2331],{"class":558,"line":1995},[556,2329,2330],{"class":649},"      id",[556,2332,796],{"class":645},[556,2334,2335,2337,2339,2341,2343,2345],{"class":558,"line":2001},[556,2336,1909],{"class":706},[556,2338,710],{"class":645},[556,2340,897],{"class":649},[556,2342,875],{"class":645},[556,2344,984],{"class":649},[556,2346,796],{"class":645},[556,2348,2350,2352,2354,2356,2358,2360,2362,2364],{"class":558,"line":2349},18,[556,2351,1925],{"class":706},[556,2353,710],{"class":645},[556,2355,897],{"class":649},[556,2357,875],{"class":645},[556,2359,878],{"class":694},[556,2361,881],{"class":706},[556,2363,727],{"class":645},[556,2365,2366],{"class":887}," \u002F\u002F renders the Mustache placeholders\n",[556,2368,2370,2372,2374],{"class":558,"line":2369},19,[556,2371,1964],{"class":645},[556,2373,859],{"class":706},[556,2375,668],{"class":645},[556,2377,2379,2381,2383,2386,2388,2390,2392,2395,2397,2399,2402,2405],{"class":558,"line":2378},20,[556,2380,1974],{"class":641},[556,2382,646],{"class":645},[556,2384,2385],{"class":706}," messageId",[556,2387,710],{"class":645},[556,2389,2151],{"class":649},[556,2391,727],{"class":645},[556,2393,2394],{"class":706}," timestamp",[556,2396,710],{"class":645},[556,2398,691],{"class":645},[556,2400,2401],{"class":694}," Date",[556,2403,2404],{"class":706},"() ",[556,2406,2407],{"class":645},"};\n",[556,2409,2411],{"class":558,"line":2410},21,[556,2412,1998],{"class":645},[556,2414,2416],{"class":558,"line":2415},22,[556,2417,1329],{"class":645},[499,2419,2420,2423],{},[503,2421,2422],{},"extends EmailTransport"," makes it the satisfier; listing the concrete class lets a controller inject it\ndirectly to read the capture store back:",[546,2425,2427],{"className":632,"code":2426,"filename":1481,"language":634,"meta":552,"style":552},"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",[503,2428,2429,2449,2470,2474,2490,2501,2511],{"__ignoreMap":552},[556,2430,2431,2433,2435,2437,2439,2441,2443,2445,2447],{"class":558,"line":559},[556,2432,642],{"class":641},[556,2434,646],{"class":645},[556,2436,1492],{"class":649},[556,2438,653],{"class":645},[556,2440,656],{"class":641},[556,2442,659],{"class":645},[556,2444,1501],{"class":566},[556,2446,665],{"class":645},[556,2448,668],{"class":645},[556,2450,2451,2453,2455,2457,2459,2461,2463,2466,2468],{"class":558,"line":671},[556,2452,642],{"class":641},[556,2454,646],{"class":645},[556,2456,2128],{"class":649},[556,2458,653],{"class":645},[556,2460,656],{"class":641},[556,2462,659],{"class":645},[556,2464,2465],{"class":566},".\u002Fcapture-email-transport",[556,2467,665],{"class":645},[556,2469,668],{"class":645},[556,2471,2472],{"class":558,"line":678},[556,2473,675],{"emptyLinePlaceholder":674},[556,2475,2476,2478,2480,2482,2484,2486,2488],{"class":558,"line":703},[556,2477,1557],{"class":641},[556,2479,1560],{"class":681},[556,2481,1563],{"class":562},[556,2483,1566],{"class":681},[556,2485,1492],{"class":694},[556,2487,697],{"class":649},[556,2489,700],{"class":645},[556,2491,2492,2494,2496,2499],{"class":558,"line":745},[556,2493,1577],{"class":706},[556,2495,710],{"class":645},[556,2497,2498],{"class":649}," [CaptureEmailTransport]",[556,2500,796],{"class":645},[556,2502,2503,2505,2507,2509],{"class":558,"line":781},[556,2504,1657],{"class":706},[556,2506,710],{"class":645},[556,2508,2498],{"class":649},[556,2510,796],{"class":645},[556,2512,2513,2515,2517],{"class":558,"line":799},[556,2514,856],{"class":645},[556,2516,1642],{"class":649},[556,2518,1673],{"class":645},[499,2520,2521,2523,2524,2526,2527,1097,2529,2531,2532,2535,2536,2538],{},[503,2522,1096],{}," catches a ",[503,2525,2033],{},", records a clean message, and returns a ",[503,2528,1119],{},[503,2530,1104],{}," — set\n",[503,2533,2534],{},"{ retryable: true, retryAfterSeconds }"," on the error to surface a transient failure to a queue consumer; any\nother thrown value becomes a non-retryable ",[503,2537,1119],{}," result under a generic \"unexpected error\" reason.",[528,2540,2542],{"id":2541},"send-from-an-event-handler","Send from an event handler",[499,2544,2545,2546,2548],{},"The most common shape: build the same kind of ",[503,2547,516],{}," in reaction to an event instead of inline in a\nrequest, so the triggering HTTP request returns immediately instead of blocking on provider latency.",[546,2550,2553],{"className":632,"code":2551,"filename":2552,"language":634,"meta":552,"style":552},"import type { IntegrationEventHandler } from \"@heximon\u002Fintegration\";\nimport { EmailMessage } from \"@heximon\u002Fnotifications\u002Femail\";\nimport { CaptureEmailTransport } from \".\u002Fcapture-email-transport\";\n\nexport class WelcomeEmailHandler implements IntegrationEventHandler\u003C\"user.signed-up\", SignedUpPayload> {\n  public constructor(private readonly transport: CaptureEmailTransport) {}\n\n  public async handle(payload: SignedUpPayload): Promise\u003Cvoid> {\n    const message = new EmailMessage({\n      from: { name: \"Heximon Example\", email: \"noreply@example.com\" },\n      to: { name: payload.name, email: payload.email },\n      subject: \"Welcome aboard!\",\n      content: \"\u003Cp>Hi {{ name }},\u003C\u002Fp>\u003Cp>Your account id is \u003Ccode>{{ userId }}\u003C\u002Fcode>.\u003C\u002Fp>\",\n      placeholders: { name: payload.name, userId: payload.userId },\n    });\n\n    await this.transport.send(message);\n  }\n}\n","welcome-email.handler.ts",[503,2554,2555,2579,2599,2619,2623,2655,2677,2681,2712,2728,2760,2793,2807,2822,2855,2863,2867,2889,2893],{"__ignoreMap":552},[556,2556,2557,2559,2561,2563,2566,2568,2570,2572,2575,2577],{"class":558,"line":559},[556,2558,642],{"class":641},[556,2560,1689],{"class":641},[556,2562,646],{"class":645},[556,2564,2565],{"class":649}," IntegrationEventHandler",[556,2567,653],{"class":645},[556,2569,656],{"class":641},[556,2571,659],{"class":645},[556,2573,2574],{"class":566},"@heximon\u002Fintegration",[556,2576,665],{"class":645},[556,2578,668],{"class":645},[556,2580,2581,2583,2585,2587,2589,2591,2593,2595,2597],{"class":558,"line":671},[556,2582,642],{"class":641},[556,2584,646],{"class":645},[556,2586,650],{"class":649},[556,2588,653],{"class":645},[556,2590,656],{"class":641},[556,2592,659],{"class":645},[556,2594,662],{"class":566},[556,2596,665],{"class":645},[556,2598,668],{"class":645},[556,2600,2601,2603,2605,2607,2609,2611,2613,2615,2617],{"class":558,"line":678},[556,2602,642],{"class":641},[556,2604,646],{"class":645},[556,2606,2128],{"class":649},[556,2608,653],{"class":645},[556,2610,656],{"class":641},[556,2612,659],{"class":645},[556,2614,2465],{"class":566},[556,2616,665],{"class":645},[556,2618,668],{"class":645},[556,2620,2621],{"class":558,"line":703},[556,2622,675],{"emptyLinePlaceholder":674},[556,2624,2625,2627,2629,2632,2635,2637,2639,2641,2644,2646,2648,2651,2653],{"class":558,"line":745},[556,2626,1557],{"class":641},[556,2628,1560],{"class":681},[556,2630,2631],{"class":562}," WelcomeEmailHandler",[556,2633,2634],{"class":681}," implements",[556,2636,2565],{"class":562},[556,2638,1821],{"class":645},[556,2640,665],{"class":645},[556,2642,2643],{"class":566},"user.signed-up",[556,2645,665],{"class":645},[556,2647,727],{"class":645},[556,2649,2650],{"class":562}," SignedUpPayload",[556,2652,1826],{"class":645},[556,2654,1276],{"class":645},[556,2656,2657,2659,2661,2663,2665,2667,2669,2671,2673,2675],{"class":558,"line":781},[556,2658,1750],{"class":681},[556,2660,1753],{"class":681},[556,2662,697],{"class":645},[556,2664,1758],{"class":681},[556,2666,1761],{"class":681},[556,2668,1178],{"class":1764},[556,2670,710],{"class":645},[556,2672,2128],{"class":562},[556,2674,859],{"class":645},[556,2676,1774],{"class":645},[556,2678,2679],{"class":558,"line":799},[556,2680,675],{"emptyLinePlaceholder":674},[556,2682,2683,2685,2687,2690,2692,2695,2697,2699,2701,2703,2705,2708,2710],{"class":558,"line":816},[556,2684,1750],{"class":681},[556,2686,1785],{"class":681},[556,2688,2689],{"class":706}," handle",[556,2691,697],{"class":645},[556,2693,2694],{"class":1764},"payload",[556,2696,710],{"class":645},[556,2698,2650],{"class":562},[556,2700,2280],{"class":645},[556,2702,1818],{"class":562},[556,2704,1821],{"class":645},[556,2706,2707],{"class":562},"void",[556,2709,1826],{"class":645},[556,2711,1276],{"class":645},[556,2713,2714,2716,2718,2720,2722,2724,2726],{"class":558,"line":853},[556,2715,1833],{"class":681},[556,2717,897],{"class":649},[556,2719,1838],{"class":645},[556,2721,691],{"class":645},[556,2723,650],{"class":694},[556,2725,697],{"class":706},[556,2727,700],{"class":645},[556,2729,2730,2732,2734,2736,2738,2740,2742,2744,2746,2748,2750,2752,2754,2756,2758],{"class":558,"line":864},[556,2731,1851],{"class":706},[556,2733,710],{"class":645},[556,2735,646],{"class":645},[556,2737,715],{"class":706},[556,2739,710],{"class":645},[556,2741,659],{"class":645},[556,2743,722],{"class":566},[556,2745,665],{"class":645},[556,2747,727],{"class":645},[556,2749,730],{"class":706},[556,2751,710],{"class":645},[556,2753,659],{"class":645},[556,2755,737],{"class":566},[556,2757,665],{"class":645},[556,2759,742],{"class":645},[556,2761,2762,2764,2766,2768,2770,2772,2775,2777,2779,2781,2783,2785,2787,2789,2791],{"class":558,"line":869},[556,2763,1873],{"class":706},[556,2765,710],{"class":645},[556,2767,646],{"class":645},[556,2769,715],{"class":706},[556,2771,710],{"class":645},[556,2773,2774],{"class":649}," payload",[556,2776,875],{"class":645},[556,2778,1889],{"class":649},[556,2780,727],{"class":645},[556,2782,730],{"class":706},[556,2784,710],{"class":645},[556,2786,2774],{"class":649},[556,2788,875],{"class":645},[556,2790,1902],{"class":649},[556,2792,742],{"class":645},[556,2794,2795,2797,2799,2801,2803,2805],{"class":558,"line":891},[556,2796,1909],{"class":706},[556,2798,710],{"class":645},[556,2800,659],{"class":645},[556,2802,791],{"class":566},[556,2804,665],{"class":645},[556,2806,796],{"class":645},[556,2808,2809,2811,2813,2815,2818,2820],{"class":558,"line":1939},[556,2810,1925],{"class":706},[556,2812,710],{"class":645},[556,2814,659],{"class":645},[556,2816,2817],{"class":566},"\u003Cp>Hi {{ name }},\u003C\u002Fp>\u003Cp>Your account id is \u003Ccode>{{ userId }}\u003C\u002Fcode>.\u003C\u002Fp>",[556,2819,665],{"class":645},[556,2821,796],{"class":645},[556,2823,2824,2826,2828,2830,2832,2834,2836,2838,2840,2842,2844,2846,2848,2850,2853],{"class":558,"line":1961},[556,2825,1942],{"class":706},[556,2827,710],{"class":645},[556,2829,646],{"class":645},[556,2831,715],{"class":706},[556,2833,710],{"class":645},[556,2835,2774],{"class":649},[556,2837,875],{"class":645},[556,2839,1889],{"class":649},[556,2841,727],{"class":645},[556,2843,839],{"class":706},[556,2845,710],{"class":645},[556,2847,2774],{"class":649},[556,2849,875],{"class":645},[556,2851,2852],{"class":649},"userId",[556,2854,742],{"class":645},[556,2856,2857,2859,2861],{"class":558,"line":1971},[556,2858,1964],{"class":645},[556,2860,859],{"class":706},[556,2862,668],{"class":645},[556,2864,2865],{"class":558,"line":1995},[556,2866,675],{"emptyLinePlaceholder":674},[556,2868,2869,2872,2874,2877,2879,2881,2883,2885,2887],{"class":558,"line":2001},[556,2870,2871],{"class":641},"    await",[556,2873,1977],{"class":645},[556,2875,2876],{"class":649},"transport",[556,2878,875],{"class":645},[556,2880,1183],{"class":694},[556,2882,697],{"class":706},[556,2884,872],{"class":649},[556,2886,859],{"class":706},[556,2888,668],{"class":645},[556,2890,2891],{"class":558,"line":2349},[556,2892,1998],{"class":645},[556,2894,2895],{"class":558,"line":2369},[556,2896,1329],{"class":645},[499,2898,2899,2900,2903,2904,2907],{},"Declare the handler under its module's ",[503,2901,2902],{},"integration.eventHandlers"," namespace, not in ",[503,2905,2906],{},"providers"," —\nthat's what dispatches it when the queue drains.",[528,2909,2911],{"id":2910},"beyond-email-sms-push","Beyond email — SMS & push",[499,2913,2914,537,2916,2918,2919,1027,2921,2923,2924,2927,2928,2931,2932,2935,2936,2939,2940,1112,2943,2946,2947,2950],{},[503,2915,516],{},[503,2917,520],{}," specialize the channel-agnostic ",[503,2920,505],{},[503,2922,509],{}," — and\nthey are not the only channel. ",[914,2925,2926],{},"SMS"," ships the same way at ",[503,2929,2930],{},"@heximon\u002Fnotifications\u002Fsms",": an ",[503,2933,2934],{},"SmsMessage","\n(E.164 addressing, GSM-7\u002FUCS-2 segment estimation) and the abstract ",[503,2937,2938],{},"SmsTransport"," token, with an edge-safe\n",[503,2941,2942],{},"TwilioSmsTransport",[503,2944,2945],{},".\u002Fsms\u002Ftwilio",") adapter that calls the Twilio REST API with ",[503,2948,2949],{},"fetch"," + form encoding —\nno SDK.",[546,2952,2954],{"className":632,"code":2953,"language":634,"meta":552,"style":552},"import { SmsTransport } from \"@heximon\u002Fnotifications\u002Fsms\";\nimport { SmsMessage } from \"@heximon\u002Fnotifications\u002Fsms\";\n\nexport class OtpService {\n  public constructor(private readonly sms: SmsTransport) {}\n\n  public async sendCode(phone: string, code: string): Promise\u003Cboolean> {\n    const result = await this.sms.send(\n      new SmsMessage({ to: phone, content: \"Your code is {{code}}.\", placeholders: { code } }),\n    );\n    return result.status === \"accepted\";\n  }\n}\n",[503,2955,2956,2977,2998,3002,3013,3036,3040,3080,3102,3153,3160,3182,3186],{"__ignoreMap":552},[556,2957,2958,2960,2962,2965,2967,2969,2971,2973,2975],{"class":558,"line":559},[556,2959,642],{"class":641},[556,2961,646],{"class":645},[556,2963,2964],{"class":649}," SmsTransport",[556,2966,653],{"class":645},[556,2968,656],{"class":641},[556,2970,659],{"class":645},[556,2972,2930],{"class":566},[556,2974,665],{"class":645},[556,2976,668],{"class":645},[556,2978,2979,2981,2983,2986,2988,2990,2992,2994,2996],{"class":558,"line":671},[556,2980,642],{"class":641},[556,2982,646],{"class":645},[556,2984,2985],{"class":649}," SmsMessage",[556,2987,653],{"class":645},[556,2989,656],{"class":641},[556,2991,659],{"class":645},[556,2993,2930],{"class":566},[556,2995,665],{"class":645},[556,2997,668],{"class":645},[556,2999,3000],{"class":558,"line":678},[556,3001,675],{"emptyLinePlaceholder":674},[556,3003,3004,3006,3008,3011],{"class":558,"line":703},[556,3005,1557],{"class":641},[556,3007,1560],{"class":681},[556,3009,3010],{"class":562}," OtpService",[556,3012,1276],{"class":645},[556,3014,3015,3017,3019,3021,3023,3025,3028,3030,3032,3034],{"class":558,"line":745},[556,3016,1750],{"class":681},[556,3018,1753],{"class":681},[556,3020,697],{"class":645},[556,3022,1758],{"class":681},[556,3024,1761],{"class":681},[556,3026,3027],{"class":1764}," sms",[556,3029,710],{"class":645},[556,3031,2964],{"class":562},[556,3033,859],{"class":645},[556,3035,1774],{"class":645},[556,3037,3038],{"class":558,"line":781},[556,3039,675],{"emptyLinePlaceholder":674},[556,3041,3042,3044,3046,3049,3051,3054,3056,3058,3060,3063,3065,3067,3069,3071,3073,3076,3078],{"class":558,"line":799},[556,3043,1750],{"class":681},[556,3045,1785],{"class":681},[556,3047,3048],{"class":706}," sendCode",[556,3050,697],{"class":645},[556,3052,3053],{"class":1764},"phone",[556,3055,710],{"class":645},[556,3057,1804],{"class":562},[556,3059,727],{"class":645},[556,3061,3062],{"class":1764}," code",[556,3064,710],{"class":645},[556,3066,1804],{"class":562},[556,3068,2280],{"class":645},[556,3070,1818],{"class":562},[556,3072,1821],{"class":645},[556,3074,3075],{"class":562},"boolean",[556,3077,1826],{"class":645},[556,3079,1276],{"class":645},[556,3081,3082,3084,3086,3088,3090,3092,3095,3097,3099],{"class":558,"line":816},[556,3083,1833],{"class":681},[556,3085,1241],{"class":649},[556,3087,1838],{"class":645},[556,3089,1175],{"class":641},[556,3091,1977],{"class":645},[556,3093,3094],{"class":649},"sms",[556,3096,875],{"class":645},[556,3098,1183],{"class":694},[556,3100,3101],{"class":706},"(\n",[556,3103,3104,3107,3109,3111,3113,3116,3118,3121,3123,3125,3127,3129,3132,3134,3136,3139,3141,3143,3145,3147,3149,3151],{"class":558,"line":853},[556,3105,3106],{"class":645},"      new",[556,3108,2985],{"class":694},[556,3110,697],{"class":706},[556,3112,1616],{"class":645},[556,3114,3115],{"class":706}," to",[556,3117,710],{"class":645},[556,3119,3120],{"class":649}," phone",[556,3122,727],{"class":645},[556,3124,2169],{"class":706},[556,3126,710],{"class":645},[556,3128,659],{"class":645},[556,3130,3131],{"class":566},"Your code is {{code}}.",[556,3133,665],{"class":645},[556,3135,727],{"class":645},[556,3137,3138],{"class":706}," placeholders",[556,3140,710],{"class":645},[556,3142,646],{"class":645},[556,3144,3062],{"class":649},[556,3146,653],{"class":645},[556,3148,653],{"class":645},[556,3150,859],{"class":706},[556,3152,796],{"class":645},[556,3154,3155,3158],{"class":558,"line":864},[556,3156,3157],{"class":706},"    )",[556,3159,668],{"class":645},[556,3161,3162,3164,3166,3168,3170,3173,3175,3178,3180],{"class":558,"line":869},[556,3163,1974],{"class":641},[556,3165,1241],{"class":649},[556,3167,875],{"class":645},[556,3169,1111],{"class":649},[556,3171,3172],{"class":645}," ===",[556,3174,659],{"class":645},[556,3176,3177],{"class":566},"accepted",[556,3179,665],{"class":645},[556,3181,668],{"class":645},[556,3183,3184],{"class":558,"line":891},[556,3185,1998],{"class":645},[556,3187,3188],{"class":558,"line":1939},[556,3189,1329],{"class":645},[499,3191,3192,3193,3195,3196,3198,3199,3201,3202,875],{},"Same ",[503,3194,1104],{},", same swap-by-binding story — bind ",[503,3197,2942],{}," to the ",[503,3200,2938],{}," token via\n",[503,3203,1473],{},[499,3205,3206,3209,3210,3213,3214,3217,3218,3221,3222,3225,3226,1027,3229,1027,3232,3235],{},[914,3207,3208],{},"Push"," ships at ",[503,3211,3212],{},"@heximon\u002Fnotifications\u002Fpush",": a ",[503,3215,3216],{},"PushMessage"," + the abstract ",[503,3219,3220],{},"PushTransport"," token + an\napp-owned ",[503,3223,3224],{},"PushDeviceRegistry"," (the framework ships no concrete device store — you implement\n",[503,3227,3228],{},"getTokensForRecipient",[503,3230,3231],{},"register",[503,3233,3234],{},"markInvalidated"," over your own database or KV).",[499,3237,3238,3239,1112,3242,3245,3246,3248,3249,1112,3252,3255,3256,1112,3259,3262,3263,3266],{},"Three edge-safe adapters ship with it — ",[503,3240,3241],{},"OneSignalPushTransport",[503,3243,3244],{},".\u002Fpush\u002Fonesignal",", one ",[503,3247,2949],{}," per send, relays to APNs\u002FFCM\u002Fweb),\n",[503,3250,3251],{},"FcmPushTransport",[503,3253,3254],{},".\u002Fpush\u002Ffcm",", FCM HTTP v1, RS256 JWT signed via WebCrypto), and a first-party\n",[503,3257,3258],{},"WebPushTransport",[503,3260,3261],{},".\u002Fpush\u002Fweb-push",", VAPID + ",[503,3264,3265],{},"aes128gcm"," encryption, entirely on WebCrypto — no SDK, no\nNode).",[499,3268,3269,3270,3273],{},"A provider that reports a dead token surfaces it on ",[503,3271,3272],{},"DeliveryResult.tokenInvalidated",", so a handler can\npurge it from the registry:",[546,3275,3278],{"className":632,"code":3276,"filename":3277,"language":634,"meta":552,"style":552},"import type { NotificationHandler } from \"@heximon\u002Fevents\";\nimport { DeliveryStatus } from \"@heximon\u002Fnotifications\";\nimport { PushDeviceRegistry, PushMessage, PushTransport } from \"@heximon\u002Fnotifications\u002Fpush\";\n\nexport class OrderShippedPushHandler implements NotificationHandler\u003COrderShipped> {\n  public constructor(\n    private readonly push: PushTransport,\n    private readonly devices: PushDeviceRegistry,\n  ) {}\n\n  public async handle(event: OrderShipped): Promise\u003Cvoid> {\n    for (const device of await this.devices.getTokensForRecipient(event.customerId)) {\n      const result = await this.push.send(\n        new PushMessage({\n          to: { token: device.token, webPushKeys: device.webPushKeys },\n          title: \"Your order shipped\",\n          content: \"Order {{orderId}} is on its way.\",\n          placeholders: { orderId: event.orderId },\n          url: \"\u002Forders\u002F{{orderId}}\",\n        }),\n      );\n\n      if (result.status === DeliveryStatus.Failed && result.tokenInvalidated === true) {\n        await this.devices.markInvalidated(event.customerId, device.token);\n      }\n    }\n  }\n}\n","order-shipped-push.handler.ts",[503,3279,3280,3304,3324,3355,3359,3381,3389,3405,3420,3427,3431,3461,3501,3522,3533,3570,3586,3602,3626,3642,3651,3658,3662,3705,3739,3745,3751,3756],{"__ignoreMap":552},[556,3281,3282,3284,3286,3288,3291,3293,3295,3297,3300,3302],{"class":558,"line":559},[556,3283,642],{"class":641},[556,3285,1689],{"class":641},[556,3287,646],{"class":645},[556,3289,3290],{"class":649}," NotificationHandler",[556,3292,653],{"class":645},[556,3294,656],{"class":641},[556,3296,659],{"class":645},[556,3298,3299],{"class":566},"@heximon\u002Fevents",[556,3301,665],{"class":645},[556,3303,668],{"class":645},[556,3305,3306,3308,3310,3312,3314,3316,3318,3320,3322],{"class":558,"line":671},[556,3307,642],{"class":641},[556,3309,646],{"class":645},[556,3311,1146],{"class":649},[556,3313,653],{"class":645},[556,3315,656],{"class":641},[556,3317,659],{"class":645},[556,3319,1155],{"class":566},[556,3321,665],{"class":645},[556,3323,668],{"class":645},[556,3325,3326,3328,3330,3333,3335,3338,3340,3343,3345,3347,3349,3351,3353],{"class":558,"line":678},[556,3327,642],{"class":641},[556,3329,646],{"class":645},[556,3331,3332],{"class":649}," PushDeviceRegistry",[556,3334,727],{"class":645},[556,3336,3337],{"class":649}," PushMessage",[556,3339,727],{"class":645},[556,3341,3342],{"class":649}," PushTransport",[556,3344,653],{"class":645},[556,3346,656],{"class":641},[556,3348,659],{"class":645},[556,3350,3212],{"class":566},[556,3352,665],{"class":645},[556,3354,668],{"class":645},[556,3356,3357],{"class":558,"line":703},[556,3358,675],{"emptyLinePlaceholder":674},[556,3360,3361,3363,3365,3368,3370,3372,3374,3377,3379],{"class":558,"line":745},[556,3362,1557],{"class":641},[556,3364,1560],{"class":681},[556,3366,3367],{"class":562}," OrderShippedPushHandler",[556,3369,2634],{"class":681},[556,3371,3290],{"class":562},[556,3373,1821],{"class":645},[556,3375,3376],{"class":562},"OrderShipped",[556,3378,1826],{"class":645},[556,3380,1276],{"class":645},[556,3382,3383,3385,3387],{"class":558,"line":781},[556,3384,1750],{"class":681},[556,3386,1753],{"class":681},[556,3388,3101],{"class":645},[556,3390,3391,3394,3396,3399,3401,3403],{"class":558,"line":799},[556,3392,3393],{"class":681},"    private",[556,3395,1761],{"class":681},[556,3397,3398],{"class":1764}," push",[556,3400,710],{"class":645},[556,3402,3342],{"class":562},[556,3404,796],{"class":645},[556,3406,3407,3409,3411,3414,3416,3418],{"class":558,"line":816},[556,3408,3393],{"class":681},[556,3410,1761],{"class":681},[556,3412,3413],{"class":1764}," devices",[556,3415,710],{"class":645},[556,3417,3332],{"class":562},[556,3419,796],{"class":645},[556,3421,3422,3425],{"class":558,"line":853},[556,3423,3424],{"class":645},"  )",[556,3426,1774],{"class":645},[556,3428,3429],{"class":558,"line":864},[556,3430,675],{"emptyLinePlaceholder":674},[556,3432,3433,3435,3437,3439,3441,3444,3446,3449,3451,3453,3455,3457,3459],{"class":558,"line":869},[556,3434,1750],{"class":681},[556,3436,1785],{"class":681},[556,3438,2689],{"class":706},[556,3440,697],{"class":645},[556,3442,3443],{"class":1764},"event",[556,3445,710],{"class":645},[556,3447,3448],{"class":562}," OrderShipped",[556,3450,2280],{"class":645},[556,3452,1818],{"class":562},[556,3454,1821],{"class":645},[556,3456,2707],{"class":562},[556,3458,1826],{"class":645},[556,3460,1276],{"class":645},[556,3462,3463,3466,3468,3470,3473,3476,3478,3480,3483,3485,3487,3489,3491,3493,3496,3499],{"class":558,"line":891},[556,3464,3465],{"class":641},"    for",[556,3467,1112],{"class":706},[556,3469,682],{"class":681},[556,3471,3472],{"class":649}," device",[556,3474,3475],{"class":645}," of",[556,3477,1175],{"class":641},[556,3479,1977],{"class":645},[556,3481,3482],{"class":649},"devices",[556,3484,875],{"class":645},[556,3486,3228],{"class":694},[556,3488,697],{"class":706},[556,3490,3443],{"class":649},[556,3492,875],{"class":645},[556,3494,3495],{"class":649},"customerId",[556,3497,3498],{"class":706},")) ",[556,3500,700],{"class":645},[556,3502,3503,3506,3508,3510,3512,3514,3516,3518,3520],{"class":558,"line":1939},[556,3504,3505],{"class":681},"      const",[556,3507,1241],{"class":649},[556,3509,1838],{"class":645},[556,3511,1175],{"class":641},[556,3513,1977],{"class":645},[556,3515,2321],{"class":649},[556,3517,875],{"class":645},[556,3519,1183],{"class":694},[556,3521,3101],{"class":706},[556,3523,3524,3527,3529,3531],{"class":558,"line":1961},[556,3525,3526],{"class":645},"        new",[556,3528,3337],{"class":694},[556,3530,697],{"class":706},[556,3532,700],{"class":645},[556,3534,3535,3538,3540,3542,3545,3547,3549,3551,3554,3556,3559,3561,3563,3565,3568],{"class":558,"line":1971},[556,3536,3537],{"class":706},"          to",[556,3539,710],{"class":645},[556,3541,646],{"class":645},[556,3543,3544],{"class":706}," token",[556,3546,710],{"class":645},[556,3548,3472],{"class":649},[556,3550,875],{"class":645},[556,3552,3553],{"class":649},"token",[556,3555,727],{"class":645},[556,3557,3558],{"class":706}," webPushKeys",[556,3560,710],{"class":645},[556,3562,3472],{"class":649},[556,3564,875],{"class":645},[556,3566,3567],{"class":649},"webPushKeys",[556,3569,742],{"class":645},[556,3571,3572,3575,3577,3579,3582,3584],{"class":558,"line":1995},[556,3573,3574],{"class":706},"          title",[556,3576,710],{"class":645},[556,3578,659],{"class":645},[556,3580,3581],{"class":566},"Your order shipped",[556,3583,665],{"class":645},[556,3585,796],{"class":645},[556,3587,3588,3591,3593,3595,3598,3600],{"class":558,"line":2001},[556,3589,3590],{"class":706},"          content",[556,3592,710],{"class":645},[556,3594,659],{"class":645},[556,3596,3597],{"class":566},"Order {{orderId}} is on its way.",[556,3599,665],{"class":645},[556,3601,796],{"class":645},[556,3603,3604,3607,3609,3611,3614,3616,3619,3621,3624],{"class":558,"line":2349},[556,3605,3606],{"class":706},"          placeholders",[556,3608,710],{"class":645},[556,3610,646],{"class":645},[556,3612,3613],{"class":706}," orderId",[556,3615,710],{"class":645},[556,3617,3618],{"class":649}," event",[556,3620,875],{"class":645},[556,3622,3623],{"class":649},"orderId",[556,3625,742],{"class":645},[556,3627,3628,3631,3633,3635,3638,3640],{"class":558,"line":2369},[556,3629,3630],{"class":706},"          url",[556,3632,710],{"class":645},[556,3634,659],{"class":645},[556,3636,3637],{"class":566},"\u002Forders\u002F{{orderId}}",[556,3639,665],{"class":645},[556,3641,796],{"class":645},[556,3643,3644,3647,3649],{"class":558,"line":2378},[556,3645,3646],{"class":645},"        }",[556,3648,859],{"class":706},[556,3650,796],{"class":645},[556,3652,3653,3656],{"class":558,"line":2410},[556,3654,3655],{"class":706},"      )",[556,3657,668],{"class":645},[556,3659,3660],{"class":558,"line":2415},[556,3661,675],{"emptyLinePlaceholder":674},[556,3663,3665,3668,3670,3673,3675,3677,3679,3681,3683,3685,3688,3690,3692,3695,3697,3701,3703],{"class":558,"line":3664},23,[556,3666,3667],{"class":641},"      if",[556,3669,1112],{"class":706},[556,3671,3672],{"class":649},"result",[556,3674,875],{"class":645},[556,3676,1111],{"class":649},[556,3678,3172],{"class":645},[556,3680,1146],{"class":649},[556,3682,875],{"class":645},[556,3684,1119],{"class":649},[556,3686,3687],{"class":645}," &&",[556,3689,1241],{"class":649},[556,3691,875],{"class":645},[556,3693,3694],{"class":649},"tokenInvalidated",[556,3696,3172],{"class":645},[556,3698,3700],{"class":3699},"sfNiH"," true",[556,3702,1642],{"class":706},[556,3704,700],{"class":645},[556,3706,3708,3711,3713,3715,3717,3719,3721,3723,3725,3727,3729,3731,3733,3735,3737],{"class":558,"line":3707},24,[556,3709,3710],{"class":641},"        await",[556,3712,1977],{"class":645},[556,3714,3482],{"class":649},[556,3716,875],{"class":645},[556,3718,3234],{"class":694},[556,3720,697],{"class":706},[556,3722,3443],{"class":649},[556,3724,875],{"class":645},[556,3726,3495],{"class":649},[556,3728,727],{"class":645},[556,3730,3472],{"class":649},[556,3732,875],{"class":645},[556,3734,3553],{"class":649},[556,3736,859],{"class":706},[556,3738,668],{"class":645},[556,3740,3742],{"class":558,"line":3741},25,[556,3743,3744],{"class":645},"      }\n",[556,3746,3748],{"class":558,"line":3747},26,[556,3749,3750],{"class":645},"    }\n",[556,3752,3754],{"class":558,"line":3753},27,[556,3755,1998],{"class":645},[556,3757,3759],{"class":558,"line":3758},28,[556,3760,1329],{"class":645},[499,3762,3763,3764,1027,3766,3768,3769,3772,3773,3775,3776,875],{},"For any other channel, subclass ",[503,3765,505],{},[503,3767,509],{}," with your own ",[503,3770,3771],{},"getChannel()"," + ",[503,3774,2025],{}," —\nyou inherit the same Mustache rendering, lifecycle, and ",[503,3777,1104],{},[528,3779,3781],{"id":3780},"render-one-template-across-channels","Render one template across channels",[499,3783,3784,1112,3787,3790,3791,1112,3794,3797,3798,3801,3802,1112,3805,3808,3809,3811,3812,3815],{},[503,3785,3786],{},"TemplateRenderer",[503,3788,3789],{},"@heximon\u002Fnotifications\u002Ftemplate",") renders one logical template per channel from a static\n",[503,3792,3793],{},"TemplateMap",[503,3795,3796],{},"templateId → locale → channel templates","). The default ",[503,3799,3800],{},"MustacheTemplateRenderer"," interpolates\nwith Mustache; locale resolution is ",[914,3803,3804],{},"per-channel",[503,3806,3807],{},"fr-CA → fr → default","), and the email ",[503,3810,984],{}," \u002F push\n",[503,3813,3814],{},"title"," are templated too:",[546,3817,3819],{"className":632,"code":3818,"language":634,"meta":552,"style":552},"import { MustacheTemplateRenderer, type TemplateMap } from \"@heximon\u002Fnotifications\u002Ftemplate\";\n\nconst templates: TemplateMap = {\n  \"order.shipped\": {\n    default: {\n      email: { subject: \"Order {{orderId}} shipped\", html: \"\u003Cp>Hi {{name}}, it's on its way.\u003C\u002Fp>\" },\n      sms: { body: \"Order {{orderId}} shipped.\" },\n      push: { title: \"Order shipped\", body: \"{{orderId}} is on its way\", url: \"\u002Forders\u002F{{orderId}}\" },\n    },\n  },\n};\n\nconst renderer = new MustacheTemplateRenderer(templates);\nawait renderer.render(\"order.shipped\", \"email\", { orderId: \"A1\", name: \"Ada\" });\n\u002F\u002F → { channel: \"email\", subject: \"Order A1 shipped\", html: \"\u003Cp>Hi Ada, it's on its way.\u003C\u002Fp>\" }\n",[503,3820,3821,3849,3853,3868,3882,3891,3927,3950,3999,4004,4009,4013,4017,4035,4097],{"__ignoreMap":552},[556,3822,3823,3825,3827,3830,3832,3834,3837,3839,3841,3843,3845,3847],{"class":558,"line":559},[556,3824,642],{"class":641},[556,3826,646],{"class":645},[556,3828,3829],{"class":649}," MustacheTemplateRenderer",[556,3831,727],{"class":645},[556,3833,1689],{"class":641},[556,3835,3836],{"class":649}," TemplateMap",[556,3838,653],{"class":645},[556,3840,656],{"class":641},[556,3842,659],{"class":645},[556,3844,3789],{"class":566},[556,3846,665],{"class":645},[556,3848,668],{"class":645},[556,3850,3851],{"class":558,"line":671},[556,3852,675],{"emptyLinePlaceholder":674},[556,3854,3855,3857,3860,3862,3864,3866],{"class":558,"line":678},[556,3856,682],{"class":681},[556,3858,3859],{"class":649}," templates",[556,3861,710],{"class":645},[556,3863,3836],{"class":562},[556,3865,1838],{"class":645},[556,3867,1276],{"class":645},[556,3869,3870,3873,3876,3878,3880],{"class":558,"line":703},[556,3871,3872],{"class":645},"  \"",[556,3874,3875],{"class":706},"order.shipped",[556,3877,665],{"class":645},[556,3879,710],{"class":645},[556,3881,1276],{"class":645},[556,3883,3884,3887,3889],{"class":558,"line":745},[556,3885,3886],{"class":706},"    default",[556,3888,710],{"class":645},[556,3890,1276],{"class":645},[556,3892,3893,3896,3898,3900,3902,3904,3906,3909,3911,3913,3916,3918,3920,3923,3925],{"class":558,"line":781},[556,3894,3895],{"class":706},"      email",[556,3897,710],{"class":645},[556,3899,646],{"class":645},[556,3901,2160],{"class":706},[556,3903,710],{"class":645},[556,3905,659],{"class":645},[556,3907,3908],{"class":566},"Order {{orderId}} shipped",[556,3910,665],{"class":645},[556,3912,727],{"class":645},[556,3914,3915],{"class":706}," html",[556,3917,710],{"class":645},[556,3919,659],{"class":645},[556,3921,3922],{"class":566},"\u003Cp>Hi {{name}}, it's on its way.\u003C\u002Fp>",[556,3924,665],{"class":645},[556,3926,742],{"class":645},[556,3928,3929,3932,3934,3936,3939,3941,3943,3946,3948],{"class":558,"line":799},[556,3930,3931],{"class":706},"      sms",[556,3933,710],{"class":645},[556,3935,646],{"class":645},[556,3937,3938],{"class":706}," body",[556,3940,710],{"class":645},[556,3942,659],{"class":645},[556,3944,3945],{"class":566},"Order {{orderId}} shipped.",[556,3947,665],{"class":645},[556,3949,742],{"class":645},[556,3951,3952,3955,3957,3959,3962,3964,3966,3969,3971,3973,3975,3977,3979,3982,3984,3986,3989,3991,3993,3995,3997],{"class":558,"line":816},[556,3953,3954],{"class":706},"      push",[556,3956,710],{"class":645},[556,3958,646],{"class":645},[556,3960,3961],{"class":706}," title",[556,3963,710],{"class":645},[556,3965,659],{"class":645},[556,3967,3968],{"class":566},"Order shipped",[556,3970,665],{"class":645},[556,3972,727],{"class":645},[556,3974,3938],{"class":706},[556,3976,710],{"class":645},[556,3978,659],{"class":645},[556,3980,3981],{"class":566},"{{orderId}} is on its way",[556,3983,665],{"class":645},[556,3985,727],{"class":645},[556,3987,3988],{"class":706}," url",[556,3990,710],{"class":645},[556,3992,659],{"class":645},[556,3994,3637],{"class":566},[556,3996,665],{"class":645},[556,3998,742],{"class":645},[556,4000,4001],{"class":558,"line":853},[556,4002,4003],{"class":645},"    },\n",[556,4005,4006],{"class":558,"line":864},[556,4007,4008],{"class":645},"  },\n",[556,4010,4011],{"class":558,"line":869},[556,4012,2407],{"class":645},[556,4014,4015],{"class":558,"line":891},[556,4016,675],{"emptyLinePlaceholder":674},[556,4018,4019,4021,4024,4026,4028,4030,4033],{"class":558,"line":1939},[556,4020,682],{"class":681},[556,4022,4023],{"class":649}," renderer ",[556,4025,688],{"class":645},[556,4027,691],{"class":645},[556,4029,3829],{"class":694},[556,4031,4032],{"class":649},"(templates)",[556,4034,668],{"class":645},[556,4036,4037,4039,4042,4044,4047,4049,4051,4053,4055,4057,4059,4061,4063,4065,4067,4069,4071,4073,4076,4078,4080,4082,4084,4086,4089,4091,4093,4095],{"class":558,"line":1961},[556,4038,894],{"class":641},[556,4040,4041],{"class":649}," renderer",[556,4043,875],{"class":645},[556,4045,4046],{"class":694},"render",[556,4048,697],{"class":649},[556,4050,665],{"class":645},[556,4052,3875],{"class":566},[556,4054,665],{"class":645},[556,4056,727],{"class":645},[556,4058,659],{"class":645},[556,4060,1902],{"class":566},[556,4062,665],{"class":645},[556,4064,727],{"class":645},[556,4066,646],{"class":645},[556,4068,3613],{"class":706},[556,4070,710],{"class":645},[556,4072,659],{"class":645},[556,4074,4075],{"class":566},"A1",[556,4077,665],{"class":645},[556,4079,727],{"class":645},[556,4081,715],{"class":706},[556,4083,710],{"class":645},[556,4085,659],{"class":645},[556,4087,4088],{"class":566},"Ada",[556,4090,665],{"class":645},[556,4092,653],{"class":645},[556,4094,859],{"class":649},[556,4096,668],{"class":645},[556,4098,4099],{"class":558,"line":1971},[556,4100,4101],{"class":887},"\u002F\u002F → { channel: \"email\", subject: \"Order A1 shipped\", html: \"\u003Cp>Hi Ada, it's on its way.\u003C\u002Fp>\" }\n",[528,4103,4105],{"id":4104},"orchestrating-across-channels","Orchestrating across channels",[499,4107,4108,4109,4112,4113,4118,4119,3772,4122,4124,4125,4128,4129,917,4132,4139],{},"Sending one message through one transport is the low level. Above it, ",[503,4110,4111],{},"@heximon\u002Fnotifications\u002Forchestration","\nturns a single channel-agnostic ",[914,4114,4115],{},[503,4116,4117],{},"Notification"," (a ",[503,4120,4121],{},"templateId",[503,4123,2694],{}," + recipient + category +\nchannels) into per-channel sends: ",[503,4126,4127],{},"NotificationDispatcher"," resolves the recipient's eligible channels\n(honoring per-recipient ",[503,4130,4131],{},"ChannelPreferences",[914,4133,4134,4135,4138],{},"bypassed for the ",[503,4136,4137],{},"System"," category",", so 2FA always sends).",[499,4141,4142,4143,4145,4146,4148,4149,4152,4153,4156],{},"It then renders each through the ",[503,4144,3786],{},", builds the channel ",[503,4147,505],{},", and sends it through the bound\ntransport — ",[503,4150,4151],{},"Fanout"," sends every eligible channel, ",[503,4154,4155],{},"Fallback"," tries them in order until one is accepted (push\nfans out over the recipient's devices).",[499,4158,4159,4160,4163,4164,4166,4167,1027,4170,4172,4173,4176],{},"Bind ",[503,4161,4162],{},"DefaultNotificationDispatcher"," via ",[503,4165,1473],{}," — its parameters are injected by type, so listing the\nchannel transports, the app's ",[503,4168,4169],{},"RecipientResolver",[503,4171,3224],{}," ports, and an optional\n",[503,4174,4175],{},"SuppressionService"," is the whole wiring:",[546,4178,4181],{"className":632,"code":4179,"filename":4180,"language":634,"meta":552,"style":552},"import { Module } from \"@heximon\u002Fruntime\";\nimport { Storage } from \"@heximon\u002Fkv\";\nimport { MemoryStorage } from \"@heximon\u002Fkv\u002Fmemory\";\nimport { EmailTransport } from \"@heximon\u002Fnotifications\u002Femail\";\nimport {\n  DefaultNotificationDispatcher,\n  NotificationDispatcher,\n  RecipientResolver,\n} from \"@heximon\u002Fnotifications\u002Forchestration\";\nimport { PushDeviceRegistry, PushTransport } from \"@heximon\u002Fnotifications\u002Fpush\";\nimport { SmsTransport } from \"@heximon\u002Fnotifications\u002Fsms\";\nimport { SuppressionService } from \"@heximon\u002Fnotifications\u002Fsuppression\";\nimport { MustacheTemplateRenderer, TemplateRenderer } from \"@heximon\u002Fnotifications\u002Ftemplate\";\nimport { AppDeviceRegistry } from \".\u002Fapp-device-registry\";\nimport { AppRecipientResolver } from \".\u002Fapp-recipient-resolver\";\nimport { CaptureEmailTransport, CapturePushTransport, CaptureSmsTransport } from \".\u002Fcapture-transports\";\nimport { templates } from \".\u002Ftemplates\";\n\nexport class NotificationsModule extends Module({\n  providers: [\n    CaptureEmailTransport,\n    CaptureSmsTransport,\n    CapturePushTransport,\n    AppRecipientResolver,\n    AppDeviceRegistry,\n    { provide: TemplateRenderer, useFactory: () => new MustacheTemplateRenderer(templates) },\n    { provide: Storage, useFactory: () => new MemoryStorage() },\n    \u002F\u002F SuppressionService is a framework class, but its constructor (the Storage port) is recovered\n    \u002F\u002F from the shipped types, so a bare provider wires it — no useFactory needed.\n    SuppressionService,\n    {\n      provide: NotificationDispatcher,\n      useFactory: (\n        renderer: TemplateRenderer,\n        recipients: RecipientResolver,\n        email: EmailTransport,\n        sms: SmsTransport,\n        push: PushTransport,\n        devices: PushDeviceRegistry,\n        suppression: SuppressionService,\n      ) =>\n        new DefaultNotificationDispatcher({\n          renderer,\n          recipients,\n          suppression,\n          email: { transport: email, from: { name: \"Heximon\", email: \"noreply@example.com\" } },\n          sms: { transport: sms },\n          push: { transport: push, devices },\n        }),\n    },\n  ],\n}) {}\n","src\u002Fnotifications\u002Fnotifications.module.ts",[503,4182,4183,4203,4225,4247,4267,4273,4280,4287,4294,4308,4332,4352,4374,4399,4421,4443,4474,4495,4499,4516,4524,4531,4538,4545,4552,4559,4588,4616,4621,4627,4635,4641,4654,4665,4677,4690,4702,4714,4726,4738,4750,4759,4771,4779,4787,4795,4846,4865,4888,4897,4902,4909],{"__ignoreMap":552},[556,4184,4185,4187,4189,4191,4193,4195,4197,4199,4201],{"class":558,"line":559},[556,4186,642],{"class":641},[556,4188,646],{"class":645},[556,4190,1492],{"class":649},[556,4192,653],{"class":645},[556,4194,656],{"class":641},[556,4196,659],{"class":645},[556,4198,1501],{"class":566},[556,4200,665],{"class":645},[556,4202,668],{"class":645},[556,4204,4205,4207,4209,4212,4214,4216,4218,4221,4223],{"class":558,"line":671},[556,4206,642],{"class":641},[556,4208,646],{"class":645},[556,4210,4211],{"class":649}," Storage",[556,4213,653],{"class":645},[556,4215,656],{"class":641},[556,4217,659],{"class":645},[556,4219,4220],{"class":566},"@heximon\u002Fkv",[556,4222,665],{"class":645},[556,4224,668],{"class":645},[556,4226,4227,4229,4231,4234,4236,4238,4240,4243,4245],{"class":558,"line":678},[556,4228,642],{"class":641},[556,4230,646],{"class":645},[556,4232,4233],{"class":649}," MemoryStorage",[556,4235,653],{"class":645},[556,4237,656],{"class":641},[556,4239,659],{"class":645},[556,4241,4242],{"class":566},"@heximon\u002Fkv\u002Fmemory",[556,4244,665],{"class":645},[556,4246,668],{"class":645},[556,4248,4249,4251,4253,4255,4257,4259,4261,4263,4265],{"class":558,"line":703},[556,4250,642],{"class":641},[556,4252,646],{"class":645},[556,4254,1514],{"class":649},[556,4256,653],{"class":645},[556,4258,656],{"class":641},[556,4260,659],{"class":645},[556,4262,662],{"class":566},[556,4264,665],{"class":645},[556,4266,668],{"class":645},[556,4268,4269,4271],{"class":558,"line":745},[556,4270,642],{"class":641},[556,4272,1276],{"class":645},[556,4274,4275,4278],{"class":558,"line":781},[556,4276,4277],{"class":649},"  DefaultNotificationDispatcher",[556,4279,796],{"class":645},[556,4281,4282,4285],{"class":558,"line":799},[556,4283,4284],{"class":649},"  NotificationDispatcher",[556,4286,796],{"class":645},[556,4288,4289,4292],{"class":558,"line":816},[556,4290,4291],{"class":649},"  RecipientResolver",[556,4293,796],{"class":645},[556,4295,4296,4298,4300,4302,4304,4306],{"class":558,"line":853},[556,4297,856],{"class":645},[556,4299,656],{"class":641},[556,4301,659],{"class":645},[556,4303,4111],{"class":566},[556,4305,665],{"class":645},[556,4307,668],{"class":645},[556,4309,4310,4312,4314,4316,4318,4320,4322,4324,4326,4328,4330],{"class":558,"line":864},[556,4311,642],{"class":641},[556,4313,646],{"class":645},[556,4315,3332],{"class":649},[556,4317,727],{"class":645},[556,4319,3342],{"class":649},[556,4321,653],{"class":645},[556,4323,656],{"class":641},[556,4325,659],{"class":645},[556,4327,3212],{"class":566},[556,4329,665],{"class":645},[556,4331,668],{"class":645},[556,4333,4334,4336,4338,4340,4342,4344,4346,4348,4350],{"class":558,"line":869},[556,4335,642],{"class":641},[556,4337,646],{"class":645},[556,4339,2964],{"class":649},[556,4341,653],{"class":645},[556,4343,656],{"class":641},[556,4345,659],{"class":645},[556,4347,2930],{"class":566},[556,4349,665],{"class":645},[556,4351,668],{"class":645},[556,4353,4354,4356,4358,4361,4363,4365,4367,4370,4372],{"class":558,"line":891},[556,4355,642],{"class":641},[556,4357,646],{"class":645},[556,4359,4360],{"class":649}," SuppressionService",[556,4362,653],{"class":645},[556,4364,656],{"class":641},[556,4366,659],{"class":645},[556,4368,4369],{"class":566},"@heximon\u002Fnotifications\u002Fsuppression",[556,4371,665],{"class":645},[556,4373,668],{"class":645},[556,4375,4376,4378,4380,4382,4384,4387,4389,4391,4393,4395,4397],{"class":558,"line":1939},[556,4377,642],{"class":641},[556,4379,646],{"class":645},[556,4381,3829],{"class":649},[556,4383,727],{"class":645},[556,4385,4386],{"class":649}," TemplateRenderer",[556,4388,653],{"class":645},[556,4390,656],{"class":641},[556,4392,659],{"class":645},[556,4394,3789],{"class":566},[556,4396,665],{"class":645},[556,4398,668],{"class":645},[556,4400,4401,4403,4405,4408,4410,4412,4414,4417,4419],{"class":558,"line":1961},[556,4402,642],{"class":641},[556,4404,646],{"class":645},[556,4406,4407],{"class":649}," AppDeviceRegistry",[556,4409,653],{"class":645},[556,4411,656],{"class":641},[556,4413,659],{"class":645},[556,4415,4416],{"class":566},".\u002Fapp-device-registry",[556,4418,665],{"class":645},[556,4420,668],{"class":645},[556,4422,4423,4425,4427,4430,4432,4434,4436,4439,4441],{"class":558,"line":1971},[556,4424,642],{"class":641},[556,4426,646],{"class":645},[556,4428,4429],{"class":649}," AppRecipientResolver",[556,4431,653],{"class":645},[556,4433,656],{"class":641},[556,4435,659],{"class":645},[556,4437,4438],{"class":566},".\u002Fapp-recipient-resolver",[556,4440,665],{"class":645},[556,4442,668],{"class":645},[556,4444,4445,4447,4449,4451,4453,4456,4458,4461,4463,4465,4467,4470,4472],{"class":558,"line":1995},[556,4446,642],{"class":641},[556,4448,646],{"class":645},[556,4450,2128],{"class":649},[556,4452,727],{"class":645},[556,4454,4455],{"class":649}," CapturePushTransport",[556,4457,727],{"class":645},[556,4459,4460],{"class":649}," CaptureSmsTransport",[556,4462,653],{"class":645},[556,4464,656],{"class":641},[556,4466,659],{"class":645},[556,4468,4469],{"class":566},".\u002Fcapture-transports",[556,4471,665],{"class":645},[556,4473,668],{"class":645},[556,4475,4476,4478,4480,4482,4484,4486,4488,4491,4493],{"class":558,"line":2001},[556,4477,642],{"class":641},[556,4479,646],{"class":645},[556,4481,3859],{"class":649},[556,4483,653],{"class":645},[556,4485,656],{"class":641},[556,4487,659],{"class":645},[556,4489,4490],{"class":566},".\u002Ftemplates",[556,4492,665],{"class":645},[556,4494,668],{"class":645},[556,4496,4497],{"class":558,"line":2349},[556,4498,675],{"emptyLinePlaceholder":674},[556,4500,4501,4503,4505,4508,4510,4512,4514],{"class":558,"line":2369},[556,4502,1557],{"class":641},[556,4504,1560],{"class":681},[556,4506,4507],{"class":562}," NotificationsModule",[556,4509,1566],{"class":681},[556,4511,1492],{"class":694},[556,4513,697],{"class":649},[556,4515,700],{"class":645},[556,4517,4518,4520,4522],{"class":558,"line":2378},[556,4519,1577],{"class":706},[556,4521,710],{"class":645},[556,4523,1582],{"class":649},[556,4525,4526,4529],{"class":558,"line":2410},[556,4527,4528],{"class":649},"    CaptureEmailTransport",[556,4530,796],{"class":645},[556,4532,4533,4536],{"class":558,"line":2415},[556,4534,4535],{"class":649},"    CaptureSmsTransport",[556,4537,796],{"class":645},[556,4539,4540,4543],{"class":558,"line":3664},[556,4541,4542],{"class":649},"    CapturePushTransport",[556,4544,796],{"class":645},[556,4546,4547,4550],{"class":558,"line":3707},[556,4548,4549],{"class":649},"    AppRecipientResolver",[556,4551,796],{"class":645},[556,4553,4554,4557],{"class":558,"line":3741},[556,4555,4556],{"class":649},"    AppDeviceRegistry",[556,4558,796],{"class":645},[556,4560,4561,4563,4565,4567,4569,4571,4573,4575,4577,4579,4581,4583,4586],{"class":558,"line":3747},[556,4562,1587],{"class":645},[556,4564,1590],{"class":706},[556,4566,710],{"class":645},[556,4568,4386],{"class":649},[556,4570,727],{"class":645},[556,4572,1599],{"class":694},[556,4574,710],{"class":645},[556,4576,1604],{"class":645},[556,4578,1607],{"class":681},[556,4580,691],{"class":645},[556,4582,3829],{"class":694},[556,4584,4585],{"class":649},"(templates) ",[556,4587,1645],{"class":645},[556,4589,4590,4592,4594,4596,4598,4600,4602,4604,4606,4608,4610,4612,4614],{"class":558,"line":3753},[556,4591,1587],{"class":645},[556,4593,1590],{"class":706},[556,4595,710],{"class":645},[556,4597,4211],{"class":649},[556,4599,727],{"class":645},[556,4601,1599],{"class":694},[556,4603,710],{"class":645},[556,4605,1604],{"class":645},[556,4607,1607],{"class":681},[556,4609,691],{"class":645},[556,4611,4233],{"class":694},[556,4613,2404],{"class":649},[556,4615,1645],{"class":645},[556,4617,4618],{"class":558,"line":3758},[556,4619,4620],{"class":887},"    \u002F\u002F SuppressionService is a framework class, but its constructor (the Storage port) is recovered\n",[556,4622,4624],{"class":558,"line":4623},29,[556,4625,4626],{"class":887},"    \u002F\u002F from the shipped types, so a bare provider wires it — no useFactory needed.\n",[556,4628,4630,4633],{"class":558,"line":4629},30,[556,4631,4632],{"class":649},"    SuppressionService",[556,4634,796],{"class":645},[556,4636,4638],{"class":558,"line":4637},31,[556,4639,4640],{"class":645},"    {\n",[556,4642,4644,4647,4649,4652],{"class":558,"line":4643},32,[556,4645,4646],{"class":706},"      provide",[556,4648,710],{"class":645},[556,4650,4651],{"class":649}," NotificationDispatcher",[556,4653,796],{"class":645},[556,4655,4657,4660,4662],{"class":558,"line":4656},33,[556,4658,4659],{"class":694},"      useFactory",[556,4661,710],{"class":645},[556,4663,4664],{"class":649}," (\n",[556,4666,4668,4671,4673,4675],{"class":558,"line":4667},34,[556,4669,4670],{"class":1764},"        renderer",[556,4672,710],{"class":645},[556,4674,4386],{"class":562},[556,4676,796],{"class":645},[556,4678,4680,4683,4685,4688],{"class":558,"line":4679},35,[556,4681,4682],{"class":1764},"        recipients",[556,4684,710],{"class":645},[556,4686,4687],{"class":562}," RecipientResolver",[556,4689,796],{"class":645},[556,4691,4693,4696,4698,4700],{"class":558,"line":4692},36,[556,4694,4695],{"class":1764},"        email",[556,4697,710],{"class":645},[556,4699,1514],{"class":562},[556,4701,796],{"class":645},[556,4703,4705,4708,4710,4712],{"class":558,"line":4704},37,[556,4706,4707],{"class":1764},"        sms",[556,4709,710],{"class":645},[556,4711,2964],{"class":562},[556,4713,796],{"class":645},[556,4715,4717,4720,4722,4724],{"class":558,"line":4716},38,[556,4718,4719],{"class":1764},"        push",[556,4721,710],{"class":645},[556,4723,3342],{"class":562},[556,4725,796],{"class":645},[556,4727,4729,4732,4734,4736],{"class":558,"line":4728},39,[556,4730,4731],{"class":1764},"        devices",[556,4733,710],{"class":645},[556,4735,3332],{"class":562},[556,4737,796],{"class":645},[556,4739,4741,4744,4746,4748],{"class":558,"line":4740},40,[556,4742,4743],{"class":1764},"        suppression",[556,4745,710],{"class":645},[556,4747,4360],{"class":562},[556,4749,796],{"class":645},[556,4751,4753,4756],{"class":558,"line":4752},41,[556,4754,4755],{"class":649},"      ) ",[556,4757,4758],{"class":681},"=>\n",[556,4760,4762,4764,4767,4769],{"class":558,"line":4761},42,[556,4763,3526],{"class":645},[556,4765,4766],{"class":694}," DefaultNotificationDispatcher",[556,4768,697],{"class":649},[556,4770,700],{"class":645},[556,4772,4774,4777],{"class":558,"line":4773},43,[556,4775,4776],{"class":649},"          renderer",[556,4778,796],{"class":645},[556,4780,4782,4785],{"class":558,"line":4781},44,[556,4783,4784],{"class":649},"          recipients",[556,4786,796],{"class":645},[556,4788,4790,4793],{"class":558,"line":4789},45,[556,4791,4792],{"class":649},"          suppression",[556,4794,796],{"class":645},[556,4796,4798,4801,4803,4805,4807,4809,4811,4813,4815,4817,4819,4821,4823,4825,4828,4830,4832,4834,4836,4838,4840,4842,4844],{"class":558,"line":4797},46,[556,4799,4800],{"class":706},"          email",[556,4802,710],{"class":645},[556,4804,646],{"class":645},[556,4806,1178],{"class":706},[556,4808,710],{"class":645},[556,4810,730],{"class":649},[556,4812,727],{"class":645},[556,4814,656],{"class":706},[556,4816,710],{"class":645},[556,4818,646],{"class":645},[556,4820,715],{"class":706},[556,4822,710],{"class":645},[556,4824,659],{"class":645},[556,4826,4827],{"class":566},"Heximon",[556,4829,665],{"class":645},[556,4831,727],{"class":645},[556,4833,730],{"class":706},[556,4835,710],{"class":645},[556,4837,659],{"class":645},[556,4839,737],{"class":566},[556,4841,665],{"class":645},[556,4843,653],{"class":645},[556,4845,742],{"class":645},[556,4847,4849,4852,4854,4856,4858,4860,4863],{"class":558,"line":4848},47,[556,4850,4851],{"class":706},"          sms",[556,4853,710],{"class":645},[556,4855,646],{"class":645},[556,4857,1178],{"class":706},[556,4859,710],{"class":645},[556,4861,4862],{"class":649}," sms ",[556,4864,1645],{"class":645},[556,4866,4868,4871,4873,4875,4877,4879,4881,4883,4886],{"class":558,"line":4867},48,[556,4869,4870],{"class":706},"          push",[556,4872,710],{"class":645},[556,4874,646],{"class":645},[556,4876,1178],{"class":706},[556,4878,710],{"class":645},[556,4880,3398],{"class":649},[556,4882,727],{"class":645},[556,4884,4885],{"class":649}," devices ",[556,4887,1645],{"class":645},[556,4889,4891,4893,4895],{"class":558,"line":4890},49,[556,4892,3646],{"class":645},[556,4894,859],{"class":649},[556,4896,796],{"class":645},[556,4898,4900],{"class":558,"line":4899},50,[556,4901,4003],{"class":645},[556,4903,4905,4907],{"class":558,"line":4904},51,[556,4906,1650],{"class":649},[556,4908,796],{"class":645},[556,4910,4912,4914,4916],{"class":558,"line":4911},52,[556,4913,856],{"class":645},[556,4915,1642],{"class":649},[556,4917,1673],{"class":645},[499,4919,4920,4921,710],{},"Dispatch a logical notification — the dispatcher renders and fans it out, returning a ",[503,4922,4923],{},"DispatchResult",[546,4925,4927],{"className":632,"code":4926,"language":634,"meta":552,"style":552},"import { uuid } from \"@heximon\u002Fruntime\";\nimport { NotificationChannel } from \"@heximon\u002Fnotifications\";\nimport { NotificationCategory, NotificationStrategy } from \"@heximon\u002Fnotifications\u002Forchestration\";\n\nconst result = await dispatcher.dispatch({\n  id: uuid.v7(),\n  type: \"order.shipped\",\n  recipientId: user.id,\n  category: NotificationCategory.Transactional,\n  channels: [NotificationChannel.Push, NotificationChannel.Email],\n  strategy: NotificationStrategy.Fallback,\n  templateId: \"order.shipped\",\n  payload: { orderId: order.id, name: user.name },\n});\n\nresult.allAccepted; \u002F\u002F true when every attempted channel was accepted\n",[503,4928,4929,4949,4970,4996,5000,5022,5039,5054,5070,5086,5111,5126,5141,5176,5184,5188],{"__ignoreMap":552},[556,4930,4931,4933,4935,4937,4939,4941,4943,4945,4947],{"class":558,"line":559},[556,4932,642],{"class":641},[556,4934,646],{"class":645},[556,4936,2049],{"class":649},[556,4938,653],{"class":645},[556,4940,656],{"class":641},[556,4942,659],{"class":645},[556,4944,1501],{"class":566},[556,4946,665],{"class":645},[556,4948,668],{"class":645},[556,4950,4951,4953,4955,4958,4960,4962,4964,4966,4968],{"class":558,"line":671},[556,4952,642],{"class":641},[556,4954,646],{"class":645},[556,4956,4957],{"class":649}," NotificationChannel",[556,4959,653],{"class":645},[556,4961,656],{"class":641},[556,4963,659],{"class":645},[556,4965,1155],{"class":566},[556,4967,665],{"class":645},[556,4969,668],{"class":645},[556,4971,4972,4974,4976,4979,4981,4984,4986,4988,4990,4992,4994],{"class":558,"line":678},[556,4973,642],{"class":641},[556,4975,646],{"class":645},[556,4977,4978],{"class":649}," NotificationCategory",[556,4980,727],{"class":645},[556,4982,4983],{"class":649}," NotificationStrategy",[556,4985,653],{"class":645},[556,4987,656],{"class":641},[556,4989,659],{"class":645},[556,4991,4111],{"class":566},[556,4993,665],{"class":645},[556,4995,668],{"class":645},[556,4997,4998],{"class":558,"line":703},[556,4999,675],{"emptyLinePlaceholder":674},[556,5001,5002,5004,5006,5008,5010,5013,5015,5018,5020],{"class":558,"line":745},[556,5003,682],{"class":681},[556,5005,1170],{"class":649},[556,5007,688],{"class":645},[556,5009,1175],{"class":641},[556,5011,5012],{"class":649}," dispatcher",[556,5014,875],{"class":645},[556,5016,5017],{"class":694},"dispatch",[556,5019,697],{"class":649},[556,5021,700],{"class":645},[556,5023,5024,5027,5029,5031,5033,5035,5037],{"class":558,"line":781},[556,5025,5026],{"class":706},"  id",[556,5028,710],{"class":645},[556,5030,2049],{"class":649},[556,5032,875],{"class":645},[556,5034,2305],{"class":694},[556,5036,881],{"class":649},[556,5038,796],{"class":645},[556,5040,5041,5044,5046,5048,5050,5052],{"class":558,"line":799},[556,5042,5043],{"class":706},"  type",[556,5045,710],{"class":645},[556,5047,659],{"class":645},[556,5049,3875],{"class":566},[556,5051,665],{"class":645},[556,5053,796],{"class":645},[556,5055,5056,5059,5061,5063,5065,5068],{"class":558,"line":816},[556,5057,5058],{"class":706},"  recipientId",[556,5060,710],{"class":645},[556,5062,1884],{"class":649},[556,5064,875],{"class":645},[556,5066,5067],{"class":649},"id",[556,5069,796],{"class":645},[556,5071,5072,5075,5077,5079,5081,5084],{"class":558,"line":853},[556,5073,5074],{"class":706},"  category",[556,5076,710],{"class":645},[556,5078,4978],{"class":649},[556,5080,875],{"class":645},[556,5082,5083],{"class":649},"Transactional",[556,5085,796],{"class":645},[556,5087,5088,5091,5093,5096,5098,5100,5102,5104,5106,5109],{"class":558,"line":864},[556,5089,5090],{"class":706},"  channels",[556,5092,710],{"class":645},[556,5094,5095],{"class":649}," [NotificationChannel",[556,5097,875],{"class":645},[556,5099,3208],{"class":649},[556,5101,727],{"class":645},[556,5103,4957],{"class":649},[556,5105,875],{"class":645},[556,5107,5108],{"class":649},"Email]",[556,5110,796],{"class":645},[556,5112,5113,5116,5118,5120,5122,5124],{"class":558,"line":869},[556,5114,5115],{"class":706},"  strategy",[556,5117,710],{"class":645},[556,5119,4983],{"class":649},[556,5121,875],{"class":645},[556,5123,4155],{"class":649},[556,5125,796],{"class":645},[556,5127,5128,5131,5133,5135,5137,5139],{"class":558,"line":891},[556,5129,5130],{"class":706},"  templateId",[556,5132,710],{"class":645},[556,5134,659],{"class":645},[556,5136,3875],{"class":566},[556,5138,665],{"class":645},[556,5140,796],{"class":645},[556,5142,5143,5146,5148,5150,5152,5154,5157,5159,5161,5163,5165,5167,5169,5171,5174],{"class":558,"line":1939},[556,5144,5145],{"class":706},"  payload",[556,5147,710],{"class":645},[556,5149,646],{"class":645},[556,5151,3613],{"class":706},[556,5153,710],{"class":645},[556,5155,5156],{"class":649}," order",[556,5158,875],{"class":645},[556,5160,5067],{"class":649},[556,5162,727],{"class":645},[556,5164,715],{"class":706},[556,5166,710],{"class":645},[556,5168,1884],{"class":649},[556,5170,875],{"class":645},[556,5172,5173],{"class":649},"name ",[556,5175,1645],{"class":645},[556,5177,5178,5180,5182],{"class":558,"line":1961},[556,5179,856],{"class":645},[556,5181,859],{"class":649},[556,5183,668],{"class":645},[556,5185,5186],{"class":558,"line":1971},[556,5187,675],{"emptyLinePlaceholder":674},[556,5189,5190,5192,5194,5197,5199],{"class":558,"line":1995},[556,5191,3672],{"class":649},[556,5193,875],{"class":645},[556,5195,5196],{"class":649},"allAccepted",[556,5198,884],{"class":645},[556,5200,5201],{"class":887}," \u002F\u002F true when every attempted channel was accepted\n",[528,5203,5205],{"id":5204},"suppress-bounces-and-complaints","Suppress bounces and complaints",[499,5207,5208,1112,5210,5212,5213,1097,5215,5218,5219,5222],{},[503,5209,4175],{},[503,5211,4369],{},", over the ",[503,5214,4220],{},[503,5216,5217],{},"Storage"," port) keeps a\nbounce\u002Fcomplaint list under ",[503,5220,5221],{},"suppress:\u003Cchannel>:\u003Crecipient>"," keys.",[499,5224,5225,5226,5229,5230,5233,5234,5236],{},"A delivery-status webhook controller\nverifies and normalizes the provider callback into a ",[503,5227,5228],{},"DeliveryEvent",", then calls ",[503,5231,5232],{},"applyDeliveryEvent"," —\nsuppressing on a hard bounce or complaint, ignoring a soft failure (which is transient and retried, not\nsuppressed). The dispatcher, when given a ",[503,5235,4175],{},", skips suppressed email\u002FSMS recipients before\nsending:",[546,5238,5240],{"className":632,"code":5239,"language":634,"meta":552,"style":552},"import { DeliveryEventKind, SuppressionService } from \"@heximon\u002Fnotifications\u002Fsuppression\";\n\nexport class ResendWebhookService {\n  public constructor(private readonly suppression: SuppressionService) {}\n\n  \u002F\u002F Called by an app HTTP controller after it has verified the webhook signature.\n  public async onEvent(event: { type: string; email: string }): Promise\u003Cvoid> {\n    if (event.type === \"email.bounced\" || event.type === \"email.complained\") {\n      await this.suppression.applyDeliveryEvent({\n        channel: \"email\",\n        recipient: event.email,\n        kind: event.type === \"email.bounced\" ? DeliveryEventKind.Bounced : DeliveryEventKind.Complained,\n      });\n    }\n  }\n}\n",[503,5241,5242,5267,5271,5282,5305,5309,5314,5357,5402,5420,5435,5450,5493,5502,5506,5510],{"__ignoreMap":552},[556,5243,5244,5246,5248,5251,5253,5255,5257,5259,5261,5263,5265],{"class":558,"line":559},[556,5245,642],{"class":641},[556,5247,646],{"class":645},[556,5249,5250],{"class":649}," DeliveryEventKind",[556,5252,727],{"class":645},[556,5254,4360],{"class":649},[556,5256,653],{"class":645},[556,5258,656],{"class":641},[556,5260,659],{"class":645},[556,5262,4369],{"class":566},[556,5264,665],{"class":645},[556,5266,668],{"class":645},[556,5268,5269],{"class":558,"line":671},[556,5270,675],{"emptyLinePlaceholder":674},[556,5272,5273,5275,5277,5280],{"class":558,"line":678},[556,5274,1557],{"class":641},[556,5276,1560],{"class":681},[556,5278,5279],{"class":562}," ResendWebhookService",[556,5281,1276],{"class":645},[556,5283,5284,5286,5288,5290,5292,5294,5297,5299,5301,5303],{"class":558,"line":703},[556,5285,1750],{"class":681},[556,5287,1753],{"class":681},[556,5289,697],{"class":645},[556,5291,1758],{"class":681},[556,5293,1761],{"class":681},[556,5295,5296],{"class":1764}," suppression",[556,5298,710],{"class":645},[556,5300,4360],{"class":562},[556,5302,859],{"class":645},[556,5304,1774],{"class":645},[556,5306,5307],{"class":558,"line":745},[556,5308,675],{"emptyLinePlaceholder":674},[556,5310,5311],{"class":558,"line":781},[556,5312,5313],{"class":887},"  \u002F\u002F Called by an app HTTP controller after it has verified the webhook signature.\n",[556,5315,5316,5318,5320,5323,5325,5327,5329,5331,5333,5335,5337,5339,5341,5343,5345,5347,5349,5351,5353,5355],{"class":558,"line":799},[556,5317,1750],{"class":681},[556,5319,1785],{"class":681},[556,5321,5322],{"class":706}," onEvent",[556,5324,697],{"class":645},[556,5326,3443],{"class":1764},[556,5328,710],{"class":645},[556,5330,646],{"class":645},[556,5332,1689],{"class":706},[556,5334,710],{"class":645},[556,5336,1804],{"class":562},[556,5338,884],{"class":645},[556,5340,730],{"class":706},[556,5342,710],{"class":645},[556,5344,1804],{"class":562},[556,5346,1815],{"class":645},[556,5348,1818],{"class":562},[556,5350,1821],{"class":645},[556,5352,2707],{"class":562},[556,5354,1826],{"class":645},[556,5356,1276],{"class":645},[556,5358,5359,5362,5364,5366,5368,5371,5373,5375,5378,5380,5383,5385,5387,5389,5391,5393,5396,5398,5400],{"class":558,"line":816},[556,5360,5361],{"class":641},"    if",[556,5363,1112],{"class":706},[556,5365,3443],{"class":649},[556,5367,875],{"class":645},[556,5369,5370],{"class":649},"type",[556,5372,3172],{"class":645},[556,5374,659],{"class":645},[556,5376,5377],{"class":566},"email.bounced",[556,5379,665],{"class":645},[556,5381,5382],{"class":645}," ||",[556,5384,3618],{"class":649},[556,5386,875],{"class":645},[556,5388,5370],{"class":649},[556,5390,3172],{"class":645},[556,5392,659],{"class":645},[556,5394,5395],{"class":566},"email.complained",[556,5397,665],{"class":645},[556,5399,1642],{"class":706},[556,5401,700],{"class":645},[556,5403,5404,5407,5409,5412,5414,5416,5418],{"class":558,"line":853},[556,5405,5406],{"class":641},"      await",[556,5408,1977],{"class":645},[556,5410,5411],{"class":649},"suppression",[556,5413,875],{"class":645},[556,5415,5232],{"class":694},[556,5417,697],{"class":706},[556,5419,700],{"class":645},[556,5421,5422,5425,5427,5429,5431,5433],{"class":558,"line":864},[556,5423,5424],{"class":706},"        channel",[556,5426,710],{"class":645},[556,5428,659],{"class":645},[556,5430,1902],{"class":566},[556,5432,665],{"class":645},[556,5434,796],{"class":645},[556,5436,5437,5440,5442,5444,5446,5448],{"class":558,"line":869},[556,5438,5439],{"class":706},"        recipient",[556,5441,710],{"class":645},[556,5443,3618],{"class":649},[556,5445,875],{"class":645},[556,5447,1902],{"class":649},[556,5449,796],{"class":645},[556,5451,5452,5455,5457,5459,5461,5463,5465,5467,5469,5471,5474,5476,5478,5481,5484,5486,5488,5491],{"class":558,"line":891},[556,5453,5454],{"class":706},"        kind",[556,5456,710],{"class":645},[556,5458,3618],{"class":649},[556,5460,875],{"class":645},[556,5462,5370],{"class":649},[556,5464,3172],{"class":645},[556,5466,659],{"class":645},[556,5468,5377],{"class":566},[556,5470,665],{"class":645},[556,5472,5473],{"class":645}," ?",[556,5475,5250],{"class":649},[556,5477,875],{"class":645},[556,5479,5480],{"class":649},"Bounced",[556,5482,5483],{"class":645}," :",[556,5485,5250],{"class":649},[556,5487,875],{"class":645},[556,5489,5490],{"class":649},"Complained",[556,5492,796],{"class":645},[556,5494,5495,5498,5500],{"class":558,"line":1939},[556,5496,5497],{"class":645},"      }",[556,5499,859],{"class":706},[556,5501,668],{"class":645},[556,5503,5504],{"class":558,"line":1961},[556,5505,3750],{"class":645},[556,5507,5508],{"class":558,"line":1971},[556,5509,1998],{"class":645},[556,5511,5512],{"class":558,"line":1995},[556,5513,1329],{"class":645},[528,5515,5517],{"id":5516},"reliability-at-scale","Reliability at scale",[499,5519,5520],{},"Three concerns beyond a single send, mostly reusing existing Heximon tiers:",[5522,5523,5524,5545,5551],"ul",{},[5525,5526,5527,1097,5530,5533,5534,5537,5538,5540,5541,5544],"li",{},[914,5528,5529],{},"Idempotency.",[503,5531,5532],{},"send(message, { idempotencyKey })"," threads a provider idempotency key (Resend's\n",[503,5535,5536],{},"Idempotency-Key"," header); ",[503,5539,4127],{}," passes the ",[503,5542,5543],{},"Notification.id"," automatically (and a\nper-device key for push, so a retry de-dupes the same device but distinct devices never collide).",[5525,5546,5547,5550],{},[914,5548,5549],{},"Suppression"," — covered above.",[5525,5552,5553,5556,5557,5560,5561,5563,5564,5567],{},[914,5554,5555],{},"Transactional outbox."," Don't send inside the request. Raise a domain event, let the DDD outbox emit it\natomically with the DB commit, have an ",[503,5558,5559],{},"IntegrationEventHandler"," enqueue the ",[503,5562,4117],{},", and a queue\nhandler call the dispatcher — at-least-once delivery with the queue's retry\u002Fbackoff\n(",[503,5565,5566],{},"DeliveryResult.retryable"," decides re-queue versus dead-letter).",[499,5569,5570,5571,5574],{},"Every transport here is fetch\u002FWebCrypto-based — the one ",[503,5572,5573],{},"node:"," reference in the package is type-only — so\nthe whole notifications stack, dispatcher included, runs on an edge deploy the same as on Node.",[528,5576,5578],{"id":5577},"see-also","See also",[5522,5580,5581,5587,5592,5597,5602,5611,5625],{},[5525,5582,5583,5586],{},[523,5584,5585],{"href":428},"Send an email"," — the shortest path: one transport, one send.",[5525,5588,5589,5591],{},[523,5590,146],{"href":147}," — when to send mail synchronously versus from an event handler, and the\nthree event tiers.",[5525,5593,5594,5596],{},[523,5595,151],{"href":152}," — hand long-lived, retryable delivery to a background queue so a slow provider\nnever stalls a request.",[5525,5598,5599,5601],{},[523,5600,185],{"href":186}," — the queue-tier interceptor that dedups a redelivered notification.",[5525,5603,5604,5607,5608,5610],{},[523,5605,5606],{"href":54},"Modules"," — how ",[503,5609,1473],{}," bindings and the abstract-token-as-DI pattern fit the DI graph.",[5525,5612,5613,5621,5622,5624],{},[523,5614,5618],{"href":5615,"rel":5616},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fgap\u002Fnotifications",[5617],"nofollow",[914,5619,5620],{},"the gap's notifications example","\n— email + SMS + push over capture transports, a ",[503,5623,4175],{},"-backed webhook controller, and an\nin-process test asserting exactly what each channel sent.",[5525,5626,5627,5634],{},[523,5628,5631],{"href":5629,"rel":5630},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fladder\u002FL08-queue",[5617],[914,5632,5633],{},"the ladder's L08 — Queue","\n— a signup emits an integration event, an event handler composes a Mustache welcome email and sends it\nthrough a capture transport, and a test asserts exactly what was sent.",[5636,5637,5638],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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);}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 .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}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 .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 .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":552,"searchDepth":671,"depth":671,"links":5640},[5641,5642,5643,5644,5645,5646,5647,5648,5649,5650,5651,5652],{"id":530,"depth":671,"text":531},{"id":611,"depth":671,"text":612},{"id":1090,"depth":671,"text":1091},{"id":1464,"depth":671,"text":1465},{"id":2015,"depth":671,"text":2016},{"id":2541,"depth":671,"text":2542},{"id":2910,"depth":671,"text":2911},{"id":3780,"depth":671,"text":3781},{"id":4104,"depth":671,"text":4105},{"id":5204,"depth":671,"text":5205},{"id":5516,"depth":671,"text":5517},{"id":5577,"depth":671,"text":5578},"Multi-channel notifications behind one Message \u002F MessageTransport contract — email (Resend, unemail), SMS (Twilio), push (OneSignal, FCM, Web Push), TemplateRenderer, SuppressionService, and the NotificationDispatcher.","md",null,{},{"icon":183},{"title":180,"description":5653},"j0chtDLGgfk0em_Py6ckjve2TgSiRqxlv72mcnA58Zs",[5661,5663],{"title":175,"path":176,"stem":177,"description":5662,"icon":178,"children":-1},"Liveness and readiness probes with HealthIndicator, HealthRegistry, and an app-owned controller — DatabaseHealthIndicator, per-indicator timeout, id-keyed results.",{"title":185,"path":186,"stem":187,"description":5664,"icon":65,"children":-1},"Idempotency tiers for event, queue, and command dispatch — EventIdempotencyInterceptor, CommandIdempotencyInterceptor, QueueIdempotencyInterceptor, Idempotency-Key header, ContextData.idempotencyKey, Storage port, best-effort dedup.",1783300761903]