[{"data":1,"prerenderedAt":1796},["ShallowReactive",2],{"navigation_docs_en":3,"-en-cli-add-notification":342,"-en-cli-add-notification-surround":1791},[4,45,61,96,132,166,196,217,241,265],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F10.reference",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F10.reference\u002F1.configuration","i-lucide-settings",{"title":57,"path":58,"stem":59,"icon":60},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F10.reference\u002F2.dependencies","i-lucide-package",{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[67,72,77,82,86,91],{"title":68,"path":69,"stem":70,"icon":71},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture","i-lucide-layers",{"title":73,"path":74,"stem":75,"icon":76},"Application lifecycle","\u002Fen\u002Fconcepts\u002Fapplication-lifecycle","en\u002F2.concepts\u002F2.application-lifecycle","i-lucide-power",{"title":78,"path":79,"stem":80,"icon":81},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F3.error-model","i-lucide-shield-alert",{"title":52,"path":83,"stem":84,"icon":85},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F4.configuration","i-lucide-settings-2",{"title":87,"path":88,"stem":89,"icon":90},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F5.codegen-pipeline","i-lucide-file-json",{"title":92,"path":93,"stem":94,"icon":95},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F6.design-patterns","i-lucide-puzzle",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":44},"HTTP & Routing","i-lucide-globe","\u002Fen\u002Fhttp","en\u002F3.http",[102,107,112,117,122,127],{"title":103,"path":104,"stem":105,"icon":106},"The server core","\u002Fen\u002Fhttp\u002Fserver","en\u002F3.http\u002F1.server","i-lucide-server",{"title":108,"path":109,"stem":110,"icon":111},"The Fiber engine","\u002Fen\u002Fhttp\u002Ffiber","en\u002F3.http\u002F2.fiber","i-lucide-zap",{"title":113,"path":114,"stem":115,"icon":116},"The chi engine","\u002Fen\u002Fhttp\u002Fchi","en\u002F3.http\u002F3.chi","i-lucide-route",{"title":118,"path":119,"stem":120,"icon":121},"Error responses","\u002Fen\u002Fhttp\u002Ferror-responses","en\u002F3.http\u002F4.error-responses","i-lucide-octagon-alert",{"title":123,"path":124,"stem":125,"icon":126},"Validation","\u002Fen\u002Fhttp\u002Fvalidation","en\u002F3.http\u002F5.validation","i-lucide-badge-check",{"title":128,"path":129,"stem":130,"icon":131},"Pagination","\u002Fen\u002Fhttp\u002Fpagination","en\u002F3.http\u002F6.pagination","i-lucide-list-ordered",{"title":133,"icon":134,"path":135,"stem":136,"children":137,"page":44},"Database","i-lucide-database","\u002Fen\u002Fdatabase","en\u002F4.database",[138,142,147,151,156,161],{"title":139,"path":140,"stem":141,"icon":134},"Overview","\u002Fen\u002Fdatabase\u002Foverview","en\u002F4.database\u002F1.overview",{"title":143,"path":144,"stem":145,"icon":146},"pgx","\u002Fen\u002Fdatabase\u002Fpgx","en\u002F4.database\u002F2.pgx","i-lucide-plug",{"title":148,"path":149,"stem":150,"icon":71},"bun","\u002Fen\u002Fdatabase\u002Fbun","en\u002F4.database\u002F3.bun",{"title":152,"path":153,"stem":154,"icon":155},"Transactions","\u002Fen\u002Fdatabase\u002Ftransactions","en\u002F4.database\u002F4.transactions","i-lucide-git-merge",{"title":157,"path":158,"stem":159,"icon":160},"Migrations","\u002Fen\u002Fdatabase\u002Fmigrations","en\u002F4.database\u002F5.migrations","i-lucide-file-stack",{"title":162,"path":163,"stem":164,"icon":165},"Seeders","\u002Fen\u002Fdatabase\u002Fseeding","en\u002F4.database\u002F6.seeding","i-lucide-sprout",{"title":167,"icon":168,"path":169,"stem":170,"children":171,"page":44},"Async & Background","i-lucide-workflow","\u002Fen\u002Fasync","en\u002F5.async",[172,176,181,186,191],{"title":173,"path":174,"stem":175,"icon":131},"Queue","\u002Fen\u002Fasync\u002Fqueue","en\u002F5.async\u002F1.queue",{"title":177,"path":178,"stem":179,"icon":180},"Events","\u002Fen\u002Fasync\u002Fevents","en\u002F5.async\u002F2.events","i-lucide-radio",{"title":182,"path":183,"stem":184,"icon":185},"Outbox","\u002Fen\u002Fasync\u002Foutbox","en\u002F5.async\u002F3.outbox","i-lucide-inbox",{"title":187,"path":188,"stem":189,"icon":190},"Scheduling","\u002Fen\u002Fasync\u002Fscheduler","en\u002F5.async\u002F4.scheduler","i-lucide-calendar-clock",{"title":192,"path":193,"stem":194,"icon":195},"Worker","\u002Fen\u002Fasync\u002Fworker","en\u002F5.async\u002F5.worker","i-lucide-cog",{"title":197,"icon":198,"path":199,"stem":200,"children":201,"page":44},"Security","i-lucide-shield-check","\u002Fen\u002Fsecurity","en\u002F6.security",[202,207,212],{"title":203,"path":204,"stem":205,"icon":206},"Authentication","\u002Fen\u002Fsecurity\u002Fauthentication","en\u002F6.security\u002F1.authentication","i-lucide-key-round",{"title":208,"path":209,"stem":210,"icon":211},"RBAC","\u002Fen\u002Fsecurity\u002Frbac","en\u002F6.security\u002F2.rbac","i-lucide-users",{"title":213,"path":214,"stem":215,"icon":216},"Policies","\u002Fen\u002Fsecurity\u002Fpolicies","en\u002F6.security\u002F3.policies","i-lucide-gavel",{"title":218,"icon":219,"path":220,"stem":221,"children":222,"page":44},"Observability","i-lucide-activity","\u002Fen\u002Fobservability","en\u002F7.observability",[223,226,231,236],{"title":139,"path":224,"stem":225,"icon":219},"\u002Fen\u002Fobservability\u002Foverview","en\u002F7.observability\u002F1.overview",{"title":227,"path":228,"stem":229,"icon":230},"Logging","\u002Fen\u002Fobservability\u002Flogging","en\u002F7.observability\u002F2.logging","i-lucide-scroll-text",{"title":232,"path":233,"stem":234,"icon":235},"OpenTelemetry","\u002Fen\u002Fobservability\u002Fopentelemetry","en\u002F7.observability\u002F3.opentelemetry","i-lucide-radar",{"title":237,"path":238,"stem":239,"icon":240},"Sentry","\u002Fen\u002Fobservability\u002Fsentry","en\u002F7.observability\u002F4.sentry","i-lucide-bug",{"title":242,"icon":243,"path":244,"stem":245,"children":246,"page":44},"Storage & Mail","i-lucide-hard-drive","\u002Fen\u002Fstorage-mail","en\u002F8.storage-mail",[247,251,256,261],{"title":248,"path":249,"stem":250,"icon":243},"Storage","\u002Fen\u002Fstorage-mail\u002Fstorage","en\u002F8.storage-mail\u002F1.storage",{"title":252,"path":253,"stem":254,"icon":255},"Email","\u002Fen\u002Fstorage-mail\u002Fmail","en\u002F8.storage-mail\u002F2.mail","i-lucide-mail",{"title":257,"path":258,"stem":259,"icon":260},"Notifications","\u002Fen\u002Fstorage-mail\u002Fnotifications","en\u002F8.storage-mail\u002F3.notifications","i-lucide-bell",{"title":262,"path":263,"stem":264,"icon":180},"Realtime","\u002Fen\u002Fstorage-mail\u002Frealtime","en\u002F8.storage-mail\u002F4.realtime",{"title":266,"icon":267,"path":268,"stem":269,"children":270,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F9.cli",[271,274,279,284,289,294,299,303,308,313,318,323,328,333,337],{"title":139,"path":272,"stem":273,"icon":267},"\u002Fen\u002Fcli\u002Foverview","en\u002F9.cli\u002F1.overview",{"title":275,"path":276,"stem":277,"icon":278},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F9.cli\u002F10.add-db","i-lucide-database-zap",{"title":280,"path":281,"stem":282,"icon":283},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F9.cli\u002F11.add-compose","i-lucide-container",{"title":285,"path":286,"stem":287,"icon":288},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F9.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":290,"path":291,"stem":292,"icon":293},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F9.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":295,"path":296,"stem":297,"icon":298},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F9.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":300,"path":301,"stem":302,"icon":165},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F9.cli\u002F15.add-seeder",{"title":304,"path":305,"stem":306,"icon":307},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F9.cli\u002F2.new-project","i-lucide-folder-plus",{"title":309,"path":310,"stem":311,"icon":312},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F9.cli\u002F3.new-module","i-lucide-blocks",{"title":314,"path":315,"stem":316,"icon":317},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F9.cli\u002F4.add-surface","i-lucide-layers-2",{"title":319,"path":320,"stem":321,"icon":322},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F9.cli\u002F5.add-core","i-lucide-box",{"title":324,"path":325,"stem":326,"icon":327},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F9.cli\u002F6.add-handler","i-lucide-webhook",{"title":329,"path":330,"stem":331,"icon":332},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F9.cli\u002F7.new-migration","i-lucide-file-plus",{"title":334,"path":335,"stem":336,"icon":195},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F9.cli\u002F8.worker-generators",{"title":338,"path":339,"stem":340,"icon":341},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F9.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"id":343,"title":290,"body":344,"description":1784,"extension":1785,"links":1786,"meta":1787,"navigation":1788,"path":291,"seo":1789,"stem":292,"__hash__":1790},"docs_en\u002Fen\u002F9.cli\u002F13.add-notification.md",{"type":345,"value":346,"toc":1777},"minimark",[347,403,451,474,481,519,523,1057,1084,1090,1133,1297,1325,1343,1490,1507,1513,1615,1619,1634,1738,1742,1773],[348,349,354],"pre",{"className":350,"code":351,"language":352,"meta":353,"style":353},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go tool gpsystem add notification \u003Cmodule> \u003Cname>\n","sh","",[355,356,357],"code",{"__ignoreMap":353},[358,359,362,366,370,373,376,379,383,386,390,393,395,398,400],"span",{"class":360,"line":361},"line",1,[358,363,365],{"class":364},"sBMFI","go",[358,367,369],{"class":368},"sfazB"," tool",[358,371,372],{"class":368}," gpsystem",[358,374,375],{"class":368}," add",[358,377,378],{"class":368}," notification",[358,380,382],{"class":381},"sMK4o"," \u003C",[358,384,385],{"class":368},"modul",[358,387,389],{"class":388},"sTEyZ","e",[358,391,392],{"class":381},">",[358,394,382],{"class":381},[358,396,397],{"class":368},"nam",[358,399,389],{"class":388},[358,401,402],{"class":381},">\n",[404,405,406,407,410,411,414,415,418,419,422,423,426,427,430,431,434,435,439,440,446,447,450],"p",{},"Creates a notification in ",[355,408,409],{},"internal\u002Fmodules\u002F\u003Cmodule>\u002Fnotifications\u002F\u003Cname>.go",": a payload struct, ",[355,412,413],{},"Via"," (defaulting to ",[355,416,417],{},"[database, mail]","), a ",[355,420,421],{},"ToMail"," and a ",[355,424,425],{},"ToDatabase",", plus its ",[355,428,429],{},"templates\u002F\u003Cname>.html.tmpl"," + ",[355,432,433],{},"templates\u002F\u003Cname>.txt.tmpl",". On a project's ",[436,437,438],"strong",{},"first"," notification (in any module) it also wires the shared ",[441,442,443],"a",{"href":258},[355,444,445],{},"notify.Sender"," into every entry point and adds the ",[355,448,449],{},"notifications"," table migration. Idempotent: a second notification, in the same or a different module, reuses the wiring.",[348,452,454],{"className":350,"code":453,"language":352,"meta":353,"style":353},"go tool gpsystem add notification contact orderShipped\n",[355,455,456],{"__ignoreMap":353},[358,457,458,460,462,464,466,468,471],{"class":360,"line":361},[358,459,365],{"class":364},[358,461,369],{"class":368},[358,463,372],{"class":368},[358,465,375],{"class":368},[358,467,378],{"class":368},[358,469,470],{"class":368}," contact",[358,472,473],{"class":368}," orderShipped\n",[475,476,478,479],"h2",{"id":477},"why-the-split-from-add-mail","Why the split from ",[355,480,285],{},[404,482,483,487,488,490,491,495,496,498,499,502,503,506,507,510,511,513,514,518],{},[441,484,485],{"href":286},[355,486,285],{}," is for content sent straight to an address, with no recipient concept: a contact-form confirmation, a password-reset link. ",[355,489,290],{}," is for content addressed to a ",[492,493,494],"em",{},"recipient"," (a user, identified by a stable id), possibly delivered on several channels at once. The two are independent and compose: a notification's ",[355,497,421],{}," builds on the same ",[355,500,501],{},"mail.Message","\u002F",[355,504,505],{},"mail.Template"," a mail notifier's ",[355,508,509],{},"Notify"," does; see the ",[441,512,257],{"href":258}," page and its ",[441,515,517],{"href":516},"\u002Fen\u002Fstorage-mail\u002Fnotifications#worked-example-the-contact-email-flow","worked example"," for a case using both side by side.",[475,520,522],{"id":521},"what-it-generates","What it generates",[348,524,528],{"className":525,"code":526,"filename":527,"language":365,"meta":353,"style":353},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package notifications\n\n\u002F\u002Fgo:embed templates\u002Fordershipped.html.tmpl templates\u002Fordershipped.txt.tmpl\nvar orderShippedTemplatesFS embed.FS\n\ntype OrderShipped struct {\n}\n\nvar (\n    _ notify.Notification = OrderShipped{}\n    _ notify.Mailable      = OrderShipped{}\n    _ notify.Databasable   = OrderShipped{}\n)\n\nfunc (OrderShipped) NotificationName() string { return \"contact.order_shipped\" }\n\nfunc (OrderShipped) Via(ctx context.Context, r notify.Recipient) []string {\n    return []string{notify.ChannelDatabase, notify.ChannelMail}\n}\n\nfunc (n OrderShipped) ToMail(ctx context.Context, r notify.Recipient) (*kitmail.Message, error) {\n    return kitmail.NewMessage().\n        WithSubject(\"TODO: subject for OrderShipped\").\n        WithBody(kitmail.Template(orderShippedTemplatesFS, \"templates\u002Fordershipped.html.tmpl\", n).\n            WithTextTemplate(\"templates\u002Fordershipped.txt.tmpl\")), nil\n    \u002F\u002F no WithTo: the mail channel routes to r automatically\n}\n\nfunc (n OrderShipped) ToDatabase(ctx context.Context, r notify.Recipient) (any, error) {\n    return n, nil\n}\n","internal\u002Fmodules\u002Fcontact\u002Fnotifications\u002Fordershipped.go",[355,529,530,538,545,552,570,575,590,596,601,609,631,650,669,675,680,725,730,783,814,819,824,883,899,917,953,974,980,985,990,1041,1052],{"__ignoreMap":353},[358,531,532,535],{"class":360,"line":361},[358,533,534],{"class":381},"package",[358,536,537],{"class":364}," notifications\n",[358,539,541],{"class":360,"line":540},2,[358,542,544],{"emptyLinePlaceholder":543},true,"\n",[358,546,548],{"class":360,"line":547},3,[358,549,551],{"class":550},"sHwdD","\u002F\u002Fgo:embed templates\u002Fordershipped.html.tmpl templates\u002Fordershipped.txt.tmpl\n",[358,553,555,558,561,564,567],{"class":360,"line":554},4,[358,556,557],{"class":381},"var",[358,559,560],{"class":388}," orderShippedTemplatesFS ",[358,562,563],{"class":364},"embed",[358,565,566],{"class":381},".",[358,568,569],{"class":364},"FS\n",[358,571,573],{"class":360,"line":572},5,[358,574,544],{"emptyLinePlaceholder":543},[358,576,578,581,584,587],{"class":360,"line":577},6,[358,579,580],{"class":381},"type",[358,582,583],{"class":364}," OrderShipped",[358,585,586],{"class":381}," struct",[358,588,589],{"class":381}," {\n",[358,591,593],{"class":360,"line":592},7,[358,594,595],{"class":381},"}\n",[358,597,599],{"class":360,"line":598},8,[358,600,544],{"emptyLinePlaceholder":543},[358,602,604,606],{"class":360,"line":603},9,[358,605,557],{"class":381},[358,607,608],{"class":381}," (\n",[358,610,612,615,618,620,623,626,628],{"class":360,"line":611},10,[358,613,614],{"class":388},"    _ ",[358,616,617],{"class":364},"notify",[358,619,566],{"class":381},[358,621,622],{"class":364},"Notification",[358,624,625],{"class":381}," =",[358,627,583],{"class":364},[358,629,630],{"class":381},"{}\n",[358,632,634,636,638,640,643,646,648],{"class":360,"line":633},11,[358,635,614],{"class":388},[358,637,617],{"class":364},[358,639,566],{"class":381},[358,641,642],{"class":364},"Mailable",[358,644,645],{"class":381},"      =",[358,647,583],{"class":364},[358,649,630],{"class":381},[358,651,653,655,657,659,662,665,667],{"class":360,"line":652},12,[358,654,614],{"class":388},[358,656,617],{"class":364},[358,658,566],{"class":381},[358,660,661],{"class":364},"Databasable",[358,663,664],{"class":381},"   =",[358,666,583],{"class":364},[358,668,630],{"class":381},[358,670,672],{"class":360,"line":671},13,[358,673,674],{"class":381},")\n",[358,676,678],{"class":360,"line":677},14,[358,679,544],{"emptyLinePlaceholder":543},[358,681,683,686,689,692,695,699,702,706,709,713,716,719,722],{"class":360,"line":682},15,[358,684,685],{"class":381},"func",[358,687,688],{"class":381}," (",[358,690,691],{"class":364},"OrderShipped",[358,693,694],{"class":381},")",[358,696,698],{"class":697},"s2Zo4"," NotificationName",[358,700,701],{"class":381},"()",[358,703,705],{"class":704},"spNyl"," string",[358,707,708],{"class":381}," {",[358,710,712],{"class":711},"s7zQu"," return",[358,714,715],{"class":381}," \"",[358,717,718],{"class":368},"contact.order_shipped",[358,720,721],{"class":381},"\"",[358,723,724],{"class":381}," }\n",[358,726,728],{"class":360,"line":727},16,[358,729,544],{"emptyLinePlaceholder":543},[358,731,733,735,737,739,741,744,747,751,754,756,759,762,765,768,770,773,775,778,781],{"class":360,"line":732},17,[358,734,685],{"class":381},[358,736,688],{"class":381},[358,738,691],{"class":364},[358,740,694],{"class":381},[358,742,743],{"class":697}," Via",[358,745,746],{"class":381},"(",[358,748,750],{"class":749},"sHdIc","ctx",[358,752,753],{"class":364}," context",[358,755,566],{"class":381},[358,757,758],{"class":364},"Context",[358,760,761],{"class":381},",",[358,763,764],{"class":749}," r",[358,766,767],{"class":364}," notify",[358,769,566],{"class":381},[358,771,772],{"class":364},"Recipient",[358,774,694],{"class":381},[358,776,777],{"class":381}," []",[358,779,780],{"class":704},"string",[358,782,589],{"class":381},[358,784,786,789,791,793,796,798,800,803,805,807,809,812],{"class":360,"line":785},18,[358,787,788],{"class":711},"    return",[358,790,777],{"class":381},[358,792,780],{"class":704},[358,794,795],{"class":381},"{",[358,797,617],{"class":388},[358,799,566],{"class":381},[358,801,802],{"class":388},"ChannelDatabase",[358,804,761],{"class":381},[358,806,767],{"class":388},[358,808,566],{"class":381},[358,810,811],{"class":388},"ChannelMail",[358,813,595],{"class":381},[358,815,817],{"class":360,"line":816},19,[358,818,595],{"class":381},[358,820,822],{"class":360,"line":821},20,[358,823,544],{"emptyLinePlaceholder":543},[358,825,827,829,831,834,836,838,841,843,845,847,849,851,853,855,857,859,861,863,866,869,871,874,876,879,881],{"class":360,"line":826},21,[358,828,685],{"class":381},[358,830,688],{"class":381},[358,832,833],{"class":749},"n ",[358,835,691],{"class":364},[358,837,694],{"class":381},[358,839,840],{"class":697}," ToMail",[358,842,746],{"class":381},[358,844,750],{"class":749},[358,846,753],{"class":364},[358,848,566],{"class":381},[358,850,758],{"class":364},[358,852,761],{"class":381},[358,854,764],{"class":749},[358,856,767],{"class":364},[358,858,566],{"class":381},[358,860,772],{"class":364},[358,862,694],{"class":381},[358,864,865],{"class":381}," (*",[358,867,868],{"class":364},"kitmail",[358,870,566],{"class":381},[358,872,873],{"class":364},"Message",[358,875,761],{"class":381},[358,877,878],{"class":704}," error",[358,880,694],{"class":381},[358,882,589],{"class":381},[358,884,886,888,891,893,896],{"class":360,"line":885},22,[358,887,788],{"class":711},[358,889,890],{"class":388}," kitmail",[358,892,566],{"class":381},[358,894,895],{"class":697},"NewMessage",[358,897,898],{"class":381},"().\n",[358,900,902,905,907,909,912,914],{"class":360,"line":901},23,[358,903,904],{"class":697},"        WithSubject",[358,906,746],{"class":381},[358,908,721],{"class":381},[358,910,911],{"class":368},"TODO: subject for OrderShipped",[358,913,721],{"class":381},[358,915,916],{"class":381},").\n",[358,918,920,923,925,927,929,932,934,937,939,941,944,946,948,951],{"class":360,"line":919},24,[358,921,922],{"class":697},"        WithBody",[358,924,746],{"class":381},[358,926,868],{"class":388},[358,928,566],{"class":381},[358,930,931],{"class":697},"Template",[358,933,746],{"class":381},[358,935,936],{"class":388},"orderShippedTemplatesFS",[358,938,761],{"class":381},[358,940,715],{"class":381},[358,942,943],{"class":368},"templates\u002Fordershipped.html.tmpl",[358,945,721],{"class":381},[358,947,761],{"class":381},[358,949,950],{"class":388}," n",[358,952,916],{"class":381},[358,954,956,959,961,963,966,968,971],{"class":360,"line":955},25,[358,957,958],{"class":697},"            WithTextTemplate",[358,960,746],{"class":381},[358,962,721],{"class":381},[358,964,965],{"class":368},"templates\u002Fordershipped.txt.tmpl",[358,967,721],{"class":381},[358,969,970],{"class":381},")),",[358,972,973],{"class":381}," nil\n",[358,975,977],{"class":360,"line":976},26,[358,978,979],{"class":550},"    \u002F\u002F no WithTo: the mail channel routes to r automatically\n",[358,981,983],{"class":360,"line":982},27,[358,984,595],{"class":381},[358,986,988],{"class":360,"line":987},28,[358,989,544],{"emptyLinePlaceholder":543},[358,991,993,995,997,999,1001,1003,1006,1008,1010,1012,1014,1016,1018,1020,1022,1024,1026,1028,1030,1033,1035,1037,1039],{"class":360,"line":992},29,[358,994,685],{"class":381},[358,996,688],{"class":381},[358,998,833],{"class":749},[358,1000,691],{"class":364},[358,1002,694],{"class":381},[358,1004,1005],{"class":697}," ToDatabase",[358,1007,746],{"class":381},[358,1009,750],{"class":749},[358,1011,753],{"class":364},[358,1013,566],{"class":381},[358,1015,758],{"class":364},[358,1017,761],{"class":381},[358,1019,764],{"class":749},[358,1021,767],{"class":364},[358,1023,566],{"class":381},[358,1025,772],{"class":364},[358,1027,694],{"class":381},[358,1029,688],{"class":381},[358,1031,1032],{"class":364},"any",[358,1034,761],{"class":381},[358,1036,878],{"class":704},[358,1038,694],{"class":381},[358,1040,589],{"class":381},[358,1042,1044,1046,1048,1050],{"class":360,"line":1043},30,[358,1045,788],{"class":711},[358,1047,950],{"class":388},[358,1049,761],{"class":381},[358,1051,973],{"class":381},[358,1053,1055],{"class":360,"line":1054},31,[358,1056,595],{"class":381},[404,1058,1059,1060,1066,1067,1071,1072,1075,1076,1078,1079,1083],{},"Plus a test using a memory mail channel and a memory database store, and two placeholder templates you fill in. The payload struct starts empty, the same way ",[441,1061,1063],{"href":1062},"\u002Fen\u002Fcli\u002Fworker-generators#add-event",[355,1064,1065],{},"add event"," and ",[441,1068,1069],{"href":286},[355,1070,285],{}," generate empty structs: add whatever fields your templates need, and add ",[355,1073,1074],{},"notify.ChannelBroadcast"," to ",[355,1077,413],{}," once ",[441,1080,1081],{"href":296},[355,1082,295],{}," has run.",[404,1085,1086,1087,1089],{},"On the ",[436,1088,438],{}," notification in the project:",[348,1091,1094],{"className":525,"code":1092,"filename":1093,"language":365,"meta":353,"style":353},"type Dependencies struct {\n    \u002F\u002F ...\n    Notifier notify.Sender\n    \u002F\u002F gpsystem:dependencies\n}\n","internal\u002Fmodules\u002Fcontact\u002Fregister.go",[355,1095,1096,1107,1112,1124,1129],{"__ignoreMap":353},[358,1097,1098,1100,1103,1105],{"class":360,"line":361},[358,1099,580],{"class":381},[358,1101,1102],{"class":364}," Dependencies",[358,1104,586],{"class":381},[358,1106,589],{"class":381},[358,1108,1109],{"class":360,"line":540},[358,1110,1111],{"class":550},"    \u002F\u002F ...\n",[358,1113,1114,1117,1119,1121],{"class":360,"line":547},[358,1115,1116],{"class":388},"    Notifier ",[358,1118,617],{"class":364},[358,1120,566],{"class":381},[358,1122,1123],{"class":364},"Sender\n",[358,1125,1126],{"class":360,"line":554},[358,1127,1128],{"class":550},"    \u002F\u002F gpsystem:dependencies\n",[358,1130,1131],{"class":360,"line":572},[358,1132,595],{"class":381},[348,1134,1137],{"className":525,"code":1135,"filename":1136,"language":365,"meta":353,"style":353},"mailer := smtp.MustNewIfConfigured(ctx, cfg.Mail)\nnotifier := notify.NewHub(\n    notify.NewMailChannel(mailer),\n    notifydatabase.NewChannel(notifydatabase.NewStore(pg.NewDB(pool))),\n)\n\u002F\u002F ...\ndeps := contact.Dependencies{\n    \u002F\u002F ...\n    Notifier: notifier,\n    \u002F\u002F gpsystem:dependencies-init\n}\n","cmd\u002Fcontact\u002Fmain.go and cmd\u002Fworker\u002Fmain.go",[355,1138,1139,1171,1188,1206,1244,1248,1253,1270,1274,1288,1293],{"__ignoreMap":353},[358,1140,1141,1144,1147,1150,1152,1155,1157,1159,1161,1164,1166,1169],{"class":360,"line":361},[358,1142,1143],{"class":388},"mailer ",[358,1145,1146],{"class":381},":=",[358,1148,1149],{"class":388}," smtp",[358,1151,566],{"class":381},[358,1153,1154],{"class":697},"MustNewIfConfigured",[358,1156,746],{"class":381},[358,1158,750],{"class":388},[358,1160,761],{"class":381},[358,1162,1163],{"class":388}," cfg",[358,1165,566],{"class":381},[358,1167,1168],{"class":388},"Mail",[358,1170,674],{"class":381},[358,1172,1173,1176,1178,1180,1182,1185],{"class":360,"line":540},[358,1174,1175],{"class":388},"notifier ",[358,1177,1146],{"class":381},[358,1179,767],{"class":388},[358,1181,566],{"class":381},[358,1183,1184],{"class":697},"NewHub",[358,1186,1187],{"class":381},"(\n",[358,1189,1190,1193,1195,1198,1200,1203],{"class":360,"line":547},[358,1191,1192],{"class":388},"    notify",[358,1194,566],{"class":381},[358,1196,1197],{"class":697},"NewMailChannel",[358,1199,746],{"class":381},[358,1201,1202],{"class":388},"mailer",[358,1204,1205],{"class":381},"),\n",[358,1207,1208,1211,1213,1216,1218,1221,1223,1226,1228,1231,1233,1236,1238,1241],{"class":360,"line":554},[358,1209,1210],{"class":388},"    notifydatabase",[358,1212,566],{"class":381},[358,1214,1215],{"class":697},"NewChannel",[358,1217,746],{"class":381},[358,1219,1220],{"class":388},"notifydatabase",[358,1222,566],{"class":381},[358,1224,1225],{"class":697},"NewStore",[358,1227,746],{"class":381},[358,1229,1230],{"class":388},"pg",[358,1232,566],{"class":381},[358,1234,1235],{"class":697},"NewDB",[358,1237,746],{"class":381},[358,1239,1240],{"class":388},"pool",[358,1242,1243],{"class":381},"))),\n",[358,1245,1246],{"class":360,"line":572},[358,1247,674],{"class":381},[358,1249,1250],{"class":360,"line":577},[358,1251,1252],{"class":550},"\u002F\u002F ...\n",[358,1254,1255,1258,1260,1262,1264,1267],{"class":360,"line":592},[358,1256,1257],{"class":388},"deps ",[358,1259,1146],{"class":381},[358,1261,470],{"class":364},[358,1263,566],{"class":381},[358,1265,1266],{"class":364},"Dependencies",[358,1268,1269],{"class":381},"{\n",[358,1271,1272],{"class":360,"line":598},[358,1273,1111],{"class":550},[358,1275,1276,1279,1282,1285],{"class":360,"line":603},[358,1277,1278],{"class":388},"    Notifier",[358,1280,1281],{"class":381},":",[358,1283,1284],{"class":388}," notifier",[358,1286,1287],{"class":381},",\n",[358,1289,1290],{"class":360,"line":611},[358,1291,1292],{"class":550},"    \u002F\u002F gpsystem:dependencies-init\n",[358,1294,1295],{"class":360,"line":633},[358,1296,595],{"class":381},[404,1298,1299,1302,1303,1306,1307,1310,1311,1314,1315,1317,1318,1321,1322,1324],{},[355,1300,1301],{},"notifier"," is constructed ",[436,1304,1305],{},"once"," per entry point (the same pattern as the shared mailer and database pool), and shared across every module with a notification. The database channel always persists through the kit's pgx executor (",[355,1308,1309],{},"pg.NewDB(pool)","), regardless of the project's ",[355,1312,1313],{},"--db"," choice for its business modules: ",[355,1316,1240],{}," is in scope everywhere the notifier is built, whereas a ",[355,1319,1320],{},"bun.DB"," is only constructed later, inside the per-module ",[355,1323,1266],{}," literal.",[404,1326,1327,1328,1330,1331,1334,1335,1338,1339,1342],{},"If ",[355,1329,295],{}," has already run, the notifier construction also builds a ",[355,1332,1333],{},"notify\u002Fbroadcast.Publisher"," and adds ",[355,1336,1337],{},"notify.NewBroadcastChannel(...)"," to the ",[355,1340,1341],{},"Hub",", plus its closer:",[348,1344,1346],{"className":525,"code":1345,"language":365,"meta":353,"style":353},"pub := broadcast.MustNewPublisher(ctx, cfg.Realtime.Redis, cfg.Realtime.Broadcast)\nnotifier := notify.NewHub(\n    notify.NewMailChannel(mailer),\n    notifydatabase.NewChannel(notifydatabase.NewStore(pg.NewDB(pool))),\n    notify.NewBroadcastChannel(pub, cfg.Realtime.Broadcast.ChannelPrefix),\n)\n",[355,1347,1348,1395,1409,1423,1453,1486],{"__ignoreMap":353},[358,1349,1350,1353,1355,1358,1360,1363,1365,1367,1369,1371,1373,1375,1377,1380,1382,1384,1386,1388,1390,1393],{"class":360,"line":361},[358,1351,1352],{"class":388},"pub ",[358,1354,1146],{"class":381},[358,1356,1357],{"class":388}," broadcast",[358,1359,566],{"class":381},[358,1361,1362],{"class":697},"MustNewPublisher",[358,1364,746],{"class":381},[358,1366,750],{"class":388},[358,1368,761],{"class":381},[358,1370,1163],{"class":388},[358,1372,566],{"class":381},[358,1374,262],{"class":388},[358,1376,566],{"class":381},[358,1378,1379],{"class":388},"Redis",[358,1381,761],{"class":381},[358,1383,1163],{"class":388},[358,1385,566],{"class":381},[358,1387,262],{"class":388},[358,1389,566],{"class":381},[358,1391,1392],{"class":388},"Broadcast",[358,1394,674],{"class":381},[358,1396,1397,1399,1401,1403,1405,1407],{"class":360,"line":540},[358,1398,1175],{"class":388},[358,1400,1146],{"class":381},[358,1402,767],{"class":388},[358,1404,566],{"class":381},[358,1406,1184],{"class":697},[358,1408,1187],{"class":381},[358,1410,1411,1413,1415,1417,1419,1421],{"class":360,"line":547},[358,1412,1192],{"class":388},[358,1414,566],{"class":381},[358,1416,1197],{"class":697},[358,1418,746],{"class":381},[358,1420,1202],{"class":388},[358,1422,1205],{"class":381},[358,1424,1425,1427,1429,1431,1433,1435,1437,1439,1441,1443,1445,1447,1449,1451],{"class":360,"line":554},[358,1426,1210],{"class":388},[358,1428,566],{"class":381},[358,1430,1215],{"class":697},[358,1432,746],{"class":381},[358,1434,1220],{"class":388},[358,1436,566],{"class":381},[358,1438,1225],{"class":697},[358,1440,746],{"class":381},[358,1442,1230],{"class":388},[358,1444,566],{"class":381},[358,1446,1235],{"class":697},[358,1448,746],{"class":381},[358,1450,1240],{"class":388},[358,1452,1243],{"class":381},[358,1454,1455,1457,1459,1462,1464,1467,1469,1471,1473,1475,1477,1479,1481,1484],{"class":360,"line":572},[358,1456,1192],{"class":388},[358,1458,566],{"class":381},[358,1460,1461],{"class":697},"NewBroadcastChannel",[358,1463,746],{"class":381},[358,1465,1466],{"class":388},"pub",[358,1468,761],{"class":381},[358,1470,1163],{"class":388},[358,1472,566],{"class":381},[358,1474,262],{"class":388},[358,1476,566],{"class":381},[358,1478,1392],{"class":388},[358,1480,566],{"class":381},[358,1482,1483],{"class":388},"ChannelPrefix",[358,1485,1205],{"class":381},[358,1487,1488],{"class":360,"line":577},[358,1489,674],{"class":381},[1491,1492,1493,1494,1496,1497,1500,1501,1503,1504,1506],"note",{},"The notifier is constructed once per entry point, on the project's first notification. If ",[355,1495,295],{}," runs ",[492,1498,1499],{},"after"," that first notification already exists, later notifications in other modules still reuse the earlier (mail+database-only) construction rather than emitting a second, conflicting one: add the broadcast channel to that entry point by hand, or run ",[355,1502,295],{}," before your first ",[355,1505,290],{}," to get it wired automatically everywhere.",[404,1508,1509,1510,1512],{},"Also wires the migration (idempotent: skipped if a ",[355,1511,449],{}," table migration already exists):",[348,1514,1519],{"className":1515,"code":1516,"filename":1517,"language":1518,"meta":353,"style":353},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","CREATE TABLE notifications (\n    id uuid PRIMARY KEY, recipient_id text NOT NULL, name text NOT NULL,\n    payload jsonb NOT NULL DEFAULT '{}'::jsonb, read_at timestamptz,\n    created_at timestamptz NOT NULL DEFAULT now()\n);\n","migrations\u002F\u003Ctimestamp>_notifications.sql","sql",[355,1520,1521,1535,1565,1593,1610],{"__ignoreMap":353},[358,1522,1523,1527,1530,1533],{"class":360,"line":361},[358,1524,1526],{"class":1525},"sbssI","CREATE",[358,1528,1529],{"class":1525}," TABLE",[358,1531,1532],{"class":697}," notifications",[358,1534,608],{"class":388},[358,1536,1537,1540,1543,1546,1549,1552,1555,1558,1561,1563],{"class":360,"line":540},[358,1538,1539],{"class":388},"    id uuid ",[358,1541,1542],{"class":704},"PRIMARY KEY",[358,1544,1545],{"class":388},", recipient_id ",[358,1547,1548],{"class":704},"text",[358,1550,1551],{"class":1525}," NOT NULL",[358,1553,1554],{"class":388},", ",[358,1556,1557],{"class":1525},"name",[358,1559,1560],{"class":704}," text",[358,1562,1551],{"class":1525},[358,1564,1287],{"class":388},[358,1566,1567,1570,1573,1576,1579,1582,1585,1588,1591],{"class":360,"line":547},[358,1568,1569],{"class":388},"    payload jsonb ",[358,1571,1572],{"class":1525},"NOT NULL",[358,1574,1575],{"class":704}," DEFAULT",[358,1577,1578],{"class":381}," '",[358,1580,1581],{"class":368},"{}",[358,1583,1584],{"class":381},"'",[358,1586,1587],{"class":388},"::jsonb, read_at ",[358,1589,1590],{"class":704},"timestamptz",[358,1592,1287],{"class":388},[358,1594,1595,1598,1600,1602,1604,1607],{"class":360,"line":554},[358,1596,1597],{"class":388},"    created_at ",[358,1599,1590],{"class":704},[358,1601,1551],{"class":1525},[358,1603,1575],{"class":704},[358,1605,1606],{"class":1525}," now",[358,1608,1609],{"class":381},"()\n",[358,1611,1612],{"class":360,"line":572},[358,1613,1614],{"class":388},");\n",[475,1616,1618],{"id":1617},"sending-it","Sending it",[404,1620,1621,1622,1626,1627,1633],{},"Nothing calls the notification automatically: send it from a service (synchronous) or a listener (asynchronous, via ",[441,1623,1624],{"href":1062},[355,1625,1065],{}," \u002F ",[441,1628,1630],{"href":1629},"\u002Fen\u002Fcli\u002Fworker-generators#add-listener",[355,1631,1632],{},"add listener","):",[348,1635,1638],{"className":525,"code":1636,"filename":1637,"language":365,"meta":353,"style":353},"return deps.Notifier.Send(ctx,\n    notify.Recipient{ID: identity.Subject, Email: identity.Email, Name: identity.Username},\n    contactnotifications.OrderShipped{ \u002F* ... *\u002F },\n)\n","internal\u002Fmodules\u002Fcontact\u002Flisteners\u002Fsendordershipped.go",[355,1639,1640,1664,1717,1734],{"__ignoreMap":353},[358,1641,1642,1645,1648,1650,1653,1655,1658,1660,1662],{"class":360,"line":361},[358,1643,1644],{"class":711},"return",[358,1646,1647],{"class":388}," deps",[358,1649,566],{"class":381},[358,1651,1652],{"class":388},"Notifier",[358,1654,566],{"class":381},[358,1656,1657],{"class":697},"Send",[358,1659,746],{"class":381},[358,1661,750],{"class":388},[358,1663,1287],{"class":381},[358,1665,1666,1668,1670,1672,1674,1677,1679,1682,1684,1687,1689,1692,1694,1696,1698,1700,1702,1705,1707,1709,1711,1714],{"class":360,"line":540},[358,1667,1192],{"class":364},[358,1669,566],{"class":381},[358,1671,772],{"class":364},[358,1673,795],{"class":381},[358,1675,1676],{"class":388},"ID",[358,1678,1281],{"class":381},[358,1680,1681],{"class":388}," identity",[358,1683,566],{"class":381},[358,1685,1686],{"class":388},"Subject",[358,1688,761],{"class":381},[358,1690,1691],{"class":388}," Email",[358,1693,1281],{"class":381},[358,1695,1681],{"class":388},[358,1697,566],{"class":381},[358,1699,252],{"class":388},[358,1701,761],{"class":381},[358,1703,1704],{"class":388}," Name",[358,1706,1281],{"class":381},[358,1708,1681],{"class":388},[358,1710,566],{"class":381},[358,1712,1713],{"class":388},"Username",[358,1715,1716],{"class":381},"},\n",[358,1718,1719,1722,1724,1726,1728,1731],{"class":360,"line":547},[358,1720,1721],{"class":364},"    contactnotifications",[358,1723,566],{"class":381},[358,1725,691],{"class":364},[358,1727,795],{"class":381},[358,1729,1730],{"class":550}," \u002F* ... *\u002F",[358,1732,1733],{"class":381}," },\n",[358,1735,1736],{"class":360,"line":554},[358,1737,674],{"class":381},[475,1739,1741],{"id":1740},"older-projects","Older projects",[404,1743,1744,1745,1066,1750,1554,1754,1756,1757,1554,1760,502,1763,1554,1766,1066,1769,1772],{},"Like ",[441,1746,1748],{"href":1747},"\u002Fen\u002Fcli\u002Fadd-mail#older-projects",[355,1749,285],{},[441,1751,1752],{"href":276},[355,1753,275],{},[355,1755,290],{}," writes at the ",[355,1758,1759],{},"gpsystem:pools",[355,1761,1762],{},"gpsystem:dependencies-init",[355,1764,1765],{},"gpsystem:deps-init(\u003Cmodule>)",[355,1767,1768],{},"gpsystem:dependencies",[355,1770,1771],{},"gpsystem:config"," anchors; a project generated before those anchors existed needs them added by hand first (see a freshly generated project for placement).",[1774,1775,1776],"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 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 .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 .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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}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 .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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":353,"searchDepth":540,"depth":540,"links":1778},[1779,1781,1782,1783],{"id":477,"depth":540,"text":1780},"Why the split from add mail",{"id":521,"depth":540,"text":522},{"id":1617,"depth":540,"text":1618},{"id":1740,"depth":540,"text":1741},"Generate a module-owned, multi-channel notification: payload, Via, per-channel content, and the shared Hub wiring.","md",null,{},{"icon":293},{"title":290,"description":1784},"-gzLMCbmokNqM9iC4B792vsNA0NlVX_V1ULeDim32dw",[1792,1794],{"title":285,"path":286,"stem":287,"description":1793,"icon":288,"children":-1},"Generate a module-owned mail notifier: payload, templates, and the shared transport wiring.",{"title":295,"path":296,"stem":297,"description":1795,"icon":298,"children":-1},"Scaffold the realtime notification gateway: a dedicated, horizontally scalable cmd\u002Frealtime binary.",1784668711480]