-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
331 lines (259 loc) · 69.9 KB
/
Copy pathindex.html
File metadata and controls
331 lines (259 loc) · 69.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
<!DOCTYPE html><html lang="en" dir="ltr" data-theme="dark" data-has-hero class="astro-eh4z3wgb"> <head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>Triggery | Triggery</title><link rel="canonical" href="https://triggeryjs.github.io/"/><link rel="alternate" hreflang="en" href="https://triggeryjs.github.io/"/><link rel="alternate" hreflang="ru" href="https://triggeryjs.github.io/ru/"/><link rel="alternate" hreflang="x-default" href="https://triggeryjs.github.io/"/><link rel="sitemap" href="/sitemap-index.xml"/><link rel="shortcut icon" href="/favicon.png" type="image/png"/><meta name="generator" content="Astro v6.3.7"/><meta name="generator" content="Starlight v0.39.2"/><meta property="og:title" content="Triggery"/><meta property="og:type" content="article"/><meta property="og:url" content="https://triggeryjs.github.io/"/><meta property="og:locale" content="en"/><meta property="og:description" content="Declarative business-logic orchestration. Framework-agnostic core with React, Solid and Vue bindings."/><meta property="og:site_name" content="Triggery"/><meta name="twitter:card" content="summary_large_image"/><meta name="description" content="Declarative business-logic orchestration. Framework-agnostic core with React, Solid and Vue bindings."/><meta name="twitter:site" content="@triggeryjs"/> <script>
"use strict";(()=>{(()=>{let t="theme-toggle",n;function r(){return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function l(){let e=localStorage.getItem(t);return e==="dark"||e==="light"?e:null}function m(){return n||l()||r()}function s(e){e===r()?localStorage.removeItem(t):localStorage.setItem(t,e)}function h(e){let o=document.documentElement;o.classList.toggle("dark",e==="dark"),o.dataset.theme=e,o.style.colorScheme=e}function a(e){n=e,s(e),h(e)}function c(){a(m())}c(),document.addEventListener("astro:after-swap",c),window.astroThemeToggle={setTheme:a,getTheme:m}})();})();
</script><link rel="stylesheet" href="/_astro/print.DNXP8c50.css" media="print"><link rel="stylesheet" href="/_astro/common.BGseFddq.css">
<link rel="stylesheet" href="/_astro/Code.LhxEQxWe.css"><script type="module" src="/_astro/page.B_tncCx8.js"></script><style>:root[data-theme=light] .pm-tabs [data-pm-icon=bun]{fill:#c4a47d}.pm-tabs [role=tab]{display:inline-flex;align-items:center}
</style></head> <body class="astro-eh4z3wgb"> <a href="#_top" class="astro-5sy4avso">Skip to content</a> <div class="flex min-h-screen flex-col astro-pg2n5w6e"> <header class="fixed inset-0 z-(--sl-z-index-navbar) box-border h-(--sl-nav-height) w-full border-0 border-b border-solid border-(--sl-color-hairline) bg-(--sl-color-bg-nav) px-(--sl-nav-pad-x) py-(--sl-nav-pad-y) backdrop-blur astro-pg2n5w6e"> <div class="box-border flex h-full items-center gap-2"> <div class="-m-3 overflow-clip p-3 flex min-w-0"> <span class="-m-1.5 rounded-md p-1.5 **:text-lg **:font-semibold **:text-(--sl-color-text) focus-visible:outline-offset-0"> <a href="/" class="site-title sl-flex astro-udzvzhd4"> <img class="astro-udzvzhd4" alt src="/_astro/logo_trig.2gS5r1ws.webp" width="939" height="939"> <span class="astro-udzvzhd4" translate="no"> Triggery </span> </a> </span> </div> <nav class="flex flex-1 flex-row gap-4 overflow-x-auto py-3 pr-4 pl-4 text-sm font-medium max-md:**:hidden xl:gap-6 xl:pl-6"> <a class="-m-1.5 rounded-md p-1.5 text-(--sl-color-gray-3) no-underline hover:text-(--sl-color-white) focus-visible:outline-offset-0" href="/guide/"> Guide </a><a class="-m-1.5 rounded-md p-1.5 text-(--sl-color-gray-3) no-underline hover:text-(--sl-color-white) focus-visible:outline-offset-0" href="/recipes/"> Recipes </a><a class="-m-1.5 rounded-md p-1.5 text-(--sl-color-gray-3) no-underline hover:text-(--sl-color-white) focus-visible:outline-offset-0" href="/examples/"> Examples </a><a class="-m-1.5 rounded-md p-1.5 text-(--sl-color-gray-3) no-underline hover:text-(--sl-color-white) focus-visible:outline-offset-0" href="/api/"> API </a><a class="-m-1.5 rounded-md p-1.5 text-(--sl-color-gray-3) no-underline hover:text-(--sl-color-white) focus-visible:outline-offset-0" href="/packages/"> Packages </a><a class="-m-1.5 rounded-md p-1.5 text-(--sl-color-gray-3) no-underline hover:text-(--sl-color-white) focus-visible:outline-offset-0" href="/ecosystem/"> Ecosystem </a> </nav> <div class="flex md:max-w-60 md:flex-1 print:hidden"> <site-search class=" astro-hysxvwck" data-translations="{"placeholder":"Search"}"> <button class="flex size-8 rounded-md p-2 text-(--sl-color-text) transition hover:bg-gray-400/30 focus-visible:outline-offset-1 active:scale-90 md:h-9 md:w-full md:max-w-88 md:rounded-md md:border md:border-solid md:border-(--sl-color-gray-5) md:p-2 md:text-(--sl-color-text) md:transition-colors md:hover:bg-gray-400/10 astro-hysxvwck" data-open-modal disabled aria-label="Search" aria-keyshortcuts="Control+K"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="size-4 astro-hysxvwck"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m21 21l-4.34-4.34" /><circle cx="11" cy="11" r="8" /></g></svg> <span class="sl-hidden md:sl-block astro-hysxvwck" aria-hidden="true">Search</span> <kbd class="sl-hidden md:sl-flex astro-hysxvwck" style="display: none;"> <kbd class="astro-hysxvwck">Ctrl</kbd><kbd class="astro-hysxvwck">K</kbd> </kbd> </button> <dialog style="padding:0" aria-label="Search" class="astro-hysxvwck"> <div class="dialog-frame sl-flex astro-hysxvwck"> <button data-close-modal class="sl-flex md:sl-hidden astro-hysxvwck"> Cancel </button> <div class="search-container astro-hysxvwck"> <div id="starlight__search" class="astro-hysxvwck"></div> </div> </div> </dialog> </site-search> <script>
;(() => {
const openBtn = document.querySelector('button[data-open-modal]')
const shortcut = openBtn?.querySelector('kbd')
if (!openBtn || !(shortcut instanceof HTMLElement)) return
const platformKey = shortcut.querySelector('kbd')
if (platformKey && /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
platformKey.textContent = '⌘'
openBtn.setAttribute('aria-keyshortcuts', 'Meta+K')
}
shortcut.style.display = ''
})()
</script> <script type="module" src="/_astro/Search.astro_astro_type_script_index_0_lang.CZEeVnww.js"></script> </div> <div class="hidden items-center gap-2 md:flex print:hidden"> <a href="https://github.com/triggeryjs/triggery" rel="me" class="flex size-8 rounded-md p-2 text-(--sl-color-text) transition hover:bg-gray-400/30 focus-visible:outline-offset-1 active:scale-90"><span class="sr-only">GitHub</span><svg aria-hidden="true" class=" astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-color: currentColor;--sl-icon-size: 1rem;"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.83-.26.83-.57L9 21.07c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.08-.74.09-.73.09-.73 1.2.09 1.83 1.24 1.83 1.24 1.08 1.83 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.14-.3-.54-1.52.1-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.28-1.55 3.29-1.23 3.29-1.23.64 1.66.24 2.88.12 3.18a4.65 4.65 0 0 1 1.23 3.22c0 4.61-2.8 5.63-5.48 5.92.42.36.81 1.1.81 2.22l-.01 3.29c0 .31.2.69.82.57A12 12 0 0 0 12 .3Z"/></svg></a><a href="https://x.com/triggeryjs" rel="me" class="flex size-8 rounded-md p-2 text-(--sl-color-text) transition hover:bg-gray-400/30 focus-visible:outline-offset-1 active:scale-90"><span class="sr-only">X</span><svg aria-hidden="true" class=" astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-color: currentColor;--sl-icon-size: 1rem;"><path d="M 18.242188 2.25 L 21.554688 2.25 L 14.324219 10.507812 L 22.828125 21.75 L 16.171875 21.75 L 10.953125 14.933594 L 4.992188 21.75 L 1.679688 21.75 L 9.40625 12.914062 L 1.257812 2.25 L 8.082031 2.25 L 12.792969 8.480469 Z M 17.082031 19.773438 L 18.914062 19.773438 L 7.082031 4.125 L 5.113281 4.125 Z M 17.082031 19.773438 "/></svg></a><a href="https://triggeryjs.github.io/discord/" rel="me" class="flex size-8 rounded-md p-2 text-(--sl-color-text) transition hover:bg-gray-400/30 focus-visible:outline-offset-1 active:scale-90"><span class="sr-only">Discord</span><svg aria-hidden="true" class=" astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-color: currentColor;--sl-icon-size: 1rem;"><path d="M20.32 4.37a19.8 19.8 0 0 0-4.93-1.51 13.78 13.78 0 0 0-.64 1.28 18.27 18.27 0 0 0-5.5 0 12.64 12.64 0 0 0-.64-1.28h-.05A19.74 19.74 0 0 0 3.64 4.4 20.26 20.26 0 0 0 .11 18.09l.02.02a19.9 19.9 0 0 0 6.04 3.03l.04-.02a14.24 14.24 0 0 0 1.23-2.03.08.08 0 0 0-.05-.07 13.1 13.1 0 0 1-1.9-.92.08.08 0 0 1 .02-.1 10.2 10.2 0 0 0 .41-.31h.04a14.2 14.2 0 0 0 12.1 0l.04.01a9.63 9.63 0 0 0 .4.32.08.08 0 0 1-.03.1 12.29 12.29 0 0 1-1.9.91.08.08 0 0 0-.02.1 15.97 15.97 0 0 0 1.27 2.01h.04a19.84 19.84 0 0 0 6.03-3.05v-.03a20.12 20.12 0 0 0-3.57-13.69ZM8.02 15.33c-1.18 0-2.16-1.08-2.16-2.42 0-1.33.96-2.42 2.16-2.42 1.21 0 2.18 1.1 2.16 2.42 0 1.34-.96 2.42-2.16 2.42Zm7.97 0c-1.18 0-2.15-1.08-2.15-2.42 0-1.33.95-2.42 2.15-2.42 1.22 0 2.18 1.1 2.16 2.42 0 1.34-.94 2.42-2.16 2.42Z"/></svg></a> <starlight-lang-select><label style="--sl-select-width: 7em" class="astro-o3xuctja"> <span class="sr-only astro-o3xuctja">Select language</span> <svg aria-hidden="true" class="icon label-icon astro-o3xuctja astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path fill-rule="evenodd" d="M8.516 3a.94.94 0 0 0-.941.94v1.15H2.94a.94.94 0 1 0 0 1.882h7.362a7.422 7.422 0 0 1-1.787 3.958 7.42 7.42 0 0 1-1.422-2.425.94.94 0 1 0-1.774.627 9.303 9.303 0 0 0 1.785 3.043 7.422 7.422 0 0 1-4.164 1.278.94.94 0 1 0 0 1.881 9.303 9.303 0 0 0 5.575-1.855 9.303 9.303 0 0 0 4.11 1.74l-.763 1.525a.968.968 0 0 0-.016.034l-1.385 2.77a.94.94 0 1 0 1.683.841l1.133-2.267h5.806l1.134 2.267a.94.94 0 0 0 1.683-.841l-1.385-2.769a.95.95 0 0 0-.018-.036l-3.476-6.951a.94.94 0 0 0-1.682 0l-1.82 3.639a7.423 7.423 0 0 1-3.593-1.256 9.303 9.303 0 0 0 2.27-5.203h1.894a.94.94 0 0 0 0-1.881H9.456V3.94A.94.94 0 0 0 8.516 3Zm6.426 11.794a1.068 1.068 0 0 1-.02.039l-.703 1.407h3.924l-1.962-3.924-1.24 2.478Z" clip-rule="evenodd"/></svg> <select autocomplete="off" class="astro-o3xuctja"> <option value="/" class="astro-o3xuctja">English</option><option value="/ru/" class="astro-o3xuctja">Русский</option> </select> <svg aria-hidden="true" class="icon caret astro-o3xuctja astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M17 9.17a1 1 0 0 0-1.41 0L12 12.71 8.46 9.17a1 1 0 1 0-1.41 1.42l4.24 4.24a1.002 1.002 0 0 0 1.42 0L17 10.59a1.002 1.002 0 0 0 0-1.42Z"/></svg> </label></starlight-lang-select><script type="module">class s extends HTMLElement{constructor(){super();const e=this.querySelector("select");e&&(e.addEventListener("change",t=>{t.currentTarget instanceof HTMLSelectElement&&(window.location.pathname=t.currentTarget.value)}),window.addEventListener("pageshow",t=>{if(!t.persisted)return;const n=e.querySelector("option[selected]")?.index;n!==e.selectedIndex&&(e.selectedIndex=n??0)}))}}customElements.define("starlight-lang-select",s);</script> <div class="size-8 overflow-visible"> <astro-theme-toggle class="flex size-8 rounded-md p-2 text-(--sl-color-text) transition hover:bg-gray-400/30 focus-visible:outline-offset-1 active:scale-90"> <div class="astro-theme-toggle-icon-light"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="size-4"><!-- Icon from Tabler Icons by Paweł Kuna - https://github.com/tabler/tabler-icons/blob/master/LICENSE --><path fill="currentColor" d="M12 19a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1m-4.95-2.05a1 1 0 0 1 0 1.414l-1.414 1.414a1 1 0 1 1-1.414-1.414l1.414-1.414a1 1 0 0 1 1.414 0m11.314 0l1.414 1.414a1 1 0 0 1-1.414 1.414l-1.414-1.414a1 1 0 0 1 1.414-1.414m-5.049-9.836a5 5 0 1 1-2.532 9.674a5 5 0 0 1 2.532-9.674M4 11a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2zm18 0a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2zM5.636 4.222L7.05 5.636A1 1 0 0 1 5.636 7.05L4.222 5.636a1 1 0 0 1 1.414-1.414m14.142 0a1 1 0 0 1 0 1.414L18.364 7.05a1 1 0 0 1-1.414-1.414l1.414-1.414a1 1 0 0 1 1.414 0M12 1a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0V2a1 1 0 0 1 1-1" /></svg> </div> <div class="astro-theme-toggle-icon-dark"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="size-4"><!-- Icon from Tabler Icons by Paweł Kuna - https://github.com/tabler/tabler-icons/blob/master/LICENSE --><path fill="currentColor" d="M12 1.992a10 10 0 1 0 9.236 13.838c.341-.82-.476-1.644-1.298-1.31a6.5 6.5 0 0 1-6.864-10.787l.077-.08c.551-.63.113-1.653-.758-1.653h-.266l-.068-.006z" /></svg> </div> </astro-theme-toggle> <script type="module">function u(){return typeof window>"u"?"light":window.astroThemeToggle?.getTheme?.()||"light"}function f(t){typeof window>"u"||window.astroThemeToggle?.setTheme?.(t)}function w(){const t=u();f(t==="light"?"dark":"light")}const l="astro-theme-toggle-temporary-styles",p="::view-transition-old(root), ::view-transition-new(root) { animation: none; mix-blend-mode: normal; }";function c(){document.getElementById(l)?.remove()}function T(){c();const t=document.createElement("style");t.id=l,t.textContent=p,document.head.appendChild(t)}async function y(t,n,e){const i=document;if(typeof i.startViewTransition!="function"){t();return}T();const o=i.startViewTransition(()=>{t()});await o?.ready,o?.finished?.then(c);const a=.7,m=`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><defs><radialGradient id="toggle-theme-gradient"><stop offset="${a}"/><stop offset="1" stop-opacity="0"/></radialGradient></defs><circle cx="4" cy="4" r="4" fill="url(#toggle-theme-gradient)"/></svg>`,r=`data:image/svg+xml;base64,${window.btoa(m)}`,h=window.innerWidth,g=window.innerHeight,s=Math.ceil(Math.hypot(Math.max(n,h-n),Math.max(e,g-e))/a);document.documentElement.animate({maskImage:[`url('${r}')`,`url('${r}')`],maskRepeat:["no-repeat","no-repeat"],maskPosition:[`${n}px ${e}px`,`${n-s}px ${e-s}px`],maskSize:["0",`${2*s}px`]},{duration:500,easing:"ease-in",pseudoElement:"::view-transition-new(root)"})}function d(t){y(w,t.clientX,t.clientY)}class x extends HTMLElement{connectedCallback(){this.hasAttribute("tabindex")||this.setAttribute("tabindex","0"),this.hasAttribute("role")||this.setAttribute("role","button"),this.addEventListener("click",d),this.addEventListener("keydown",n=>{if(n.key==="Enter"||n.key===" "){n.preventDefault();const e=this.getBoundingClientRect(),i=e.left+e.width/2,o=e.top+e.height/2;d({clientX:i,clientY:o})}})}}customElements.define("astro-theme-toggle",x);</script> </div> </div> </div> </header> <div class="main-frame astro-pg2n5w6e"> <script type="module">const a=document.getElementById("starlight__sidebar"),n=a?.querySelector("sl-sidebar-state-persist"),o="sl-sidebar-state",i=()=>{let t=[];const e=n?.dataset.hash||"";try{const s=sessionStorage.getItem(o),r=JSON.parse(s||"{}");Array.isArray(r.open)&&r.hash===e&&(t=r.open)}catch{}return{hash:e,open:t,scroll:a?.scrollTop||0}},c=t=>{try{sessionStorage.setItem(o,JSON.stringify(t))}catch{}},d=()=>c(i()),l=(t,e)=>{const s=i();s.open[e]=t,c(s)};n?.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const e=t.target.closest("summary")?.closest("details");if(!e)return;const s=e.querySelector("sl-sidebar-restore"),r=parseInt(s?.dataset.index||"");isNaN(r)||l(!e.open,r)});addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&d()});addEventListener("pageHide",d);</script> <div class="lg:sl-flex astro-bxxwurxa"> <div class="main-pane astro-bxxwurxa"> <main data-pagefind-body class="astro-eh4z3wgb" lang="en" dir="ltr"> <div class="content-panel astro-j4cp3mw6"> <div class="sl-container astro-j4cp3mw6"> <div class="hero astro-wnigxbaf"> <img src="/_astro/logo_trig.2gS5r1ws_ohYK4.webp" loading="eager" decoding="async" alt width="400" height="400" class="astro-wnigxbaf"> <div class="sl-flex stack astro-wnigxbaf"> <div class="sl-flex copy astro-wnigxbaf"> <h1 id="_top" data-page-title class="astro-wnigxbaf">Triggery</h1> <div class="tagline astro-wnigxbaf">Write business logic, not boilerplate.</div> </div> </div> </div> <div class="sl-markdown-content">
<div class="hero-cta"><a class="sl-link-button not-content primary astro-i25ua3fl" href="/guide/getting-started/"> Get started <svg aria-hidden="true" class="astro-i25ua3fl astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z"/></svg> </a><a class="sl-link-button not-content minimal astro-i25ua3fl" href="https://github.com/triggeryjs/triggery"> View on GitHub <svg aria-hidden="true" class="astro-i25ua3fl astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M19.33 10.18a1 1 0 0 1-.77 0 1 1 0 0 1-.62-.93l.01-1.83-8.2 8.2a1 1 0 0 1-1.41-1.42l8.2-8.2H14.7a1 1 0 0 1 0-2h4.25a1 1 0 0 1 1 1v4.25a1 1 0 0 1-.62.93Z"/><path d="M11 4a1 1 0 1 1 0 2H7a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-4a1 1 0 1 1 2 0v4a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h4Z"/></svg> </a><a class="sl-link-button not-content minimal astro-i25ua3fl" href="/discord/"> Join Discord <svg aria-hidden="true" class="astro-i25ua3fl astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M19.33 10.18a1 1 0 0 1-.77 0 1 1 0 0 1-.62-.93l.01-1.83-8.2 8.2a1 1 0 0 1-1.41-1.42l8.2-8.2H14.7a1 1 0 0 1 0-2h4.25a1 1 0 0 1 1 1v4.25a1 1 0 0 1-.62.93Z"/><path d="M11 4a1 1 0 1 1 0 2H7a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-4a1 1 0 1 1 2 0v4a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h4Z"/></svg> </a></div>
<div class="sl-heading-wrapper level-h2"><h2 id="triggery-in-30-seconds">Triggery in 30 seconds</h2><a class="sl-anchor-link" href="#triggery-in-30-seconds"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Triggery in 30 seconds”</span></a></div>
<p>One file describes one scenario. Events go in, conditions gate the world, actions perform side effects. The same hook-shaped API works in React, Solid and Vue.</p>
<div data-nova-code-container="" data-nova-code-title="src/triggers/message.trigger.ts" class="not-content relative overflow-hidden [&_pre]:relative [&_nova-code-copy-button]:opacity-0 [&:hover_nova-code-copy-button]:opacity-100 border border-solid border-(--sl-color-gray-5) rounded-md"><div class="hidden border-b border-solid border-b-(--sl-color-gray-5) bg-(--sl-color-bg) px-4 py-2 font-mono text-sm text-(--sl-color-gray-3) [div[data-nova-code-container][data-nova-code-title]_&]:block">src/triggers/message.trigger.ts</div><pre class="astro-code astro-code-themes one-light github-dark-dimmed" style="--shiki-light:#383A42;--shiki-dark:#adbac7;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#22272e; overflow-x: auto;" tabindex="0" data-language="ts" dir="ltr"><code><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">import</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> { </span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">createTrigger</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> } </span><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">from</span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF"> '@triggery/core'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">type</span><span style="--shiki-light:#C18401;--shiki-dark:#F69D50"> Settings</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> { </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">sound</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#0184BC;--shiki-dark:#6CB6FF"> boolean</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">; </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">notifications</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#0184BC;--shiki-dark:#6CB6FF"> boolean</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> };</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">type</span><span style="--shiki-light:#C18401;--shiki-dark:#F69D50"> Payload</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> { </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">author</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#0184BC;--shiki-dark:#6CB6FF"> string</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">; </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">text</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#0184BC;--shiki-dark:#6CB6FF"> string</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">; </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">channelId</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#0184BC;--shiki-dark:#6CB6FF"> string</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> };</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">export</span><span style="--shiki-light:#A626A4;--shiki-dark:#F47067"> const</span><span style="--shiki-light:#986801;--shiki-dark:#6CB6FF"> messageTrigger</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067"> =</span><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB"> createTrigger</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"><{</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#F69D50"> events</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF">'new-message'</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#C18401;--shiki-dark:#F69D50"> Payload</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> };</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#F69D50"> conditions</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> { </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">settings</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#C18401;--shiki-dark:#F69D50"> Settings</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">; </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">activeChannelId</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#383A42;--shiki-dark:#6CB6FF"> string</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067"> |</span><span style="--shiki-light:#383A42;--shiki-dark:#6CB6FF"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> };</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#F69D50"> actions</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> { </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">showToast</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> { </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">title</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#383A42;--shiki-dark:#6CB6FF"> string</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">; </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">body</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#383A42;--shiki-dark:#6CB6FF"> string</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> }; </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">playSound</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">:</span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF"> 'beep'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> };</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">}>({</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7"> id</span><span style="--shiki-light:#0184BC;--shiki-dark:#ADBAC7">:</span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF"> 'message-received'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">,</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7"> required</span><span style="--shiki-light:#0184BC;--shiki-dark:#ADBAC7">:</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> [</span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF">'settings'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">],</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB"> handler</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">({ </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">event</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">conditions</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">actions</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">check</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> }) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#F47067"> if</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> (conditions.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">activeChannelId</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067"> ===</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> event.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">payload</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">channelId</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">) </span><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">return</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#F47067"> if</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> (</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067">!</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">check.</span><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB">is</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF">'settings'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">s</span><span style="--shiki-light:#A626A4;--shiki-dark:#F47067"> =></span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> s.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">notifications</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">)) </span><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">return</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> actions.</span><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB">debounce</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">(</span><span style="--shiki-light:#986801;--shiki-dark:#6CB6FF">800</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">).</span><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB">playSound</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">?.(</span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF">'beep'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> actions.</span><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB">showToast</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">?.({ </span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">title</span><span style="--shiki-light:#0184BC;--shiki-dark:#ADBAC7">:</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> event.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">payload</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">author</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, </span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">body</span><span style="--shiki-light:#0184BC;--shiki-dark:#ADBAC7">:</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> event.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">payload</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">text</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> });</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> },</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">});</span></span></code></pre><nova-code-copy-button type="button" data-code="import { createTrigger } from '@triggery/core';
type Settings = { sound: boolean; notifications: boolean };
type Payload = { author: string; text: string; channelId: string };
export const messageTrigger = createTrigger<{
events: { 'new-message': Payload };
conditions: { settings: Settings; activeChannelId: string | null };
actions: { showToast: { title: string; body: string }; playSound: 'beep' };
}>({
id: 'message-received',
required: ['settings'],
handler({ event, conditions, actions, check }) {
if (conditions.activeChannelId === event.payload.channelId) return;
if (!check.is('settings', s => s.notifications)) return;
actions.debounce(800).playSound?.('beep');
actions.showToast?.({ title: event.payload.author, body: event.payload.text });
},
});" title="Copy code" aria-label="Copy code" class="absolute top-2 right-2 m-0 size-6 rounded border border-solid border-(--sl-color-gray-5) bg-gray-100/30 p-1 text-black backdrop-blur-sm transition hover:bg-gray-200/50 active:scale-90 dark:bg-gray-600/30 dark:text-white hover:dark:bg-gray-500/50"></nova-code-copy-button></div>
<p>Components stay focused on their own port. None of them reach into another’s state.</p>
<div data-nova-code-container="" data-nova-code-title="src/Chat.tsx" class="not-content relative overflow-hidden [&_pre]:relative [&_nova-code-copy-button]:opacity-0 [&:hover_nova-code-copy-button]:opacity-100 border border-solid border-(--sl-color-gray-5) rounded-md"><div class="hidden border-b border-solid border-b-(--sl-color-gray-5) bg-(--sl-color-bg) px-4 py-2 font-mono text-sm text-(--sl-color-gray-3) [div[data-nova-code-container][data-nova-code-title]_&]:block">src/Chat.tsx</div><pre class="astro-code astro-code-themes one-light github-dark-dimmed" style="--shiki-light:#383A42;--shiki-dark:#adbac7;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#22272e; overflow-x: auto;" tabindex="0" data-language="tsx" dir="ltr"><code><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">import</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> { </span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">useEvent</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, </span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">useCondition</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, </span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">useAction</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> } </span><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">from</span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF"> '@triggery/react'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">import</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> { </span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">messageTrigger</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> } </span><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">from</span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF"> './triggers/message.trigger'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#768390;--shiki-dark-font-style:inherit">// Producer: a chat input fires the event.</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">const</span><span style="--shiki-light:#986801;--shiki-dark:#6CB6FF"> fireNewMessage</span><span style="--shiki-light:#0184BC;--shiki-dark:#F47067"> =</span><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB"> useEvent</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">(messageTrigger, </span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF">'new-message'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#768390;--shiki-dark-font-style:inherit">// Provider: a settings panel exposes a snapshot via a getter.</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB">useCondition</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">(messageTrigger, </span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF">'settings'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, () </span><span style="--shiki-light:#A626A4;--shiki-dark:#F47067">=></span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> settings, [settings]);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#768390;--shiki-dark-font-style:inherit">// Reactor: a toast component subscribes to the action.</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB">useAction</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">(messageTrigger, </span><span style="--shiki-light:#50A14F;--shiki-dark:#96D0FF">'showToast'</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, </span><span style="--shiki-light:#383A42;--shiki-dark:#F69D50">payload</span><span style="--shiki-light:#A626A4;--shiki-dark:#F47067"> =></span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7"> toast</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#DCBDFB">success</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">(</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">payload</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">title</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">, { description</span><span style="--shiki-light:#0184BC;--shiki-dark:#ADBAC7">:</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7"> payload</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7">.</span><span style="--shiki-light:#E45649;--shiki-dark:#ADBAC7">body</span><span style="--shiki-light:#383A42;--shiki-dark:#ADBAC7"> }));</span></span></code></pre><nova-code-copy-button type="button" data-code="import { useEvent, useCondition, useAction } from '@triggery/react';
import { messageTrigger } from './triggers/message.trigger';
// Producer: a chat input fires the event.
const fireNewMessage = useEvent(messageTrigger, 'new-message');
// Provider: a settings panel exposes a snapshot via a getter.
useCondition(messageTrigger, 'settings', () => settings, [settings]);
// Reactor: a toast component subscribes to the action.
useAction(messageTrigger, 'showToast', payload => toast.success(payload.title, { description: payload.body }));" title="Copy code" aria-label="Copy code" class="absolute top-2 right-2 m-0 size-6 rounded border border-solid border-(--sl-color-gray-5) bg-gray-100/30 p-1 text-black backdrop-blur-sm transition hover:bg-gray-200/50 active:scale-90 dark:bg-gray-600/30 dark:text-white hover:dark:bg-gray-500/50"></nova-code-copy-button></div>
<p>No props drilled, no <code dir="auto">useEffect</code> chains, no shared context bus. Edit the trigger file and the whole reaction changes — every component stays the same.</p>
<div class="sl-heading-wrapper level-h2"><h2 id="why-teams-pick-triggery">Why teams pick Triggery</h2><a class="sl-anchor-link" href="#why-teams-pick-triggery"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Why teams pick Triggery”</span></a></div>
<div class="card-grid stagger astro-agdvlovl"><article class="card sl-flex astro-jkgbziii"> <p class="title sl-flex astro-jkgbziii"> <svg aria-hidden="true" class="icon astro-jkgbziii astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.333em;"><path d="m21.32 9.55-1.89-.63.89-1.78A1 1 0 0 0 20.13 6L18 3.87a1 1 0 0 0-1.15-.19l-1.78.89-.63-1.89A1 1 0 0 0 13.5 2h-3a1 1 0 0 0-.95.68l-.63 1.89-1.78-.89A1 1 0 0 0 6 3.87L3.87 6a1 1 0 0 0-.19 1.15l.89 1.78-1.89.63a1 1 0 0 0-.68.94v3a1 1 0 0 0 .68.95l1.89.63-.89 1.78A1 1 0 0 0 3.87 18L6 20.13a1 1 0 0 0 1.15.19l1.78-.89.63 1.89a1 1 0 0 0 .95.68h3a1 1 0 0 0 .95-.68l.63-1.89 1.78.89a1 1 0 0 0 1.13-.19L20.13 18a1 1 0 0 0 .19-1.15l-.89-1.78 1.89-.63a1 1 0 0 0 .68-.94v-3a1 1 0 0 0-.68-.95ZM20 12.78l-1.2.4A2 2 0 0 0 17.64 16l.57 1.14-1.1 1.1-1.11-.6a2 2 0 0 0-2.79 1.16l-.4 1.2h-1.59l-.4-1.2A2 2 0 0 0 8 17.64l-1.14.57-1.1-1.1.6-1.11a2 2 0 0 0-1.16-2.82l-1.2-.4v-1.56l1.2-.4A2 2 0 0 0 6.36 8l-.57-1.11 1.1-1.1L8 6.36a2 2 0 0 0 2.82-1.16l.4-1.2h1.56l.4 1.2A2 2 0 0 0 16 6.36l1.14-.57 1.1 1.1-.6 1.11a2 2 0 0 0 1.16 2.79l1.2.4v1.59ZM12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8Zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4Z"/></svg> <span class="astro-jkgbziii">Framework-agnostic core</span> </p> <div class="body astro-jkgbziii"><p>Zero runtime dependencies. Use <code dir="auto">@triggery/core</code> directly from Node, plain JavaScript, or any framework. Bindings are thin lifecycle layers — about 50 lines each.</p></div> </article><article class="card sl-flex astro-jkgbziii"> <p class="title sl-flex astro-jkgbziii"> <svg aria-hidden="true" class="icon astro-jkgbziii astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.333em;"><path d="M17 22H5a3 3 0 0 1-3-3V9a3 3 0 0 1 3-3h1a4 4 0 0 1 7.3-2.18c.448.64.692 1.4.7 2.18h3a1 1 0 0 1 1 1v3a4 4 0 0 1 2.18 7.3A3.86 3.86 0 0 1 18 18v3a1 1 0 0 1-1 1ZM5 8a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h11v-3.18a1 1 0 0 1 1.33-.95 1.77 1.77 0 0 0 1.74-.23 2 2 0 0 0 .93-1.37 2 2 0 0 0-.48-1.59 1.89 1.89 0 0 0-2.17-.55 1 1 0 0 1-1.33-.95V8h-3.2a1 1 0 0 1-1-1.33 1.77 1.77 0 0 0-.23-1.74 1.939 1.939 0 0 0-3-.43A2 2 0 0 0 8 6c.002.23.046.456.13.67A1 1 0 0 1 7.18 8H5Z"/></svg> <span class="astro-jkgbziii">React · Solid · Vue</span> </p> <div class="body astro-jkgbziii"><p>The same hook API in every binding. Switch your stack without rewriting the orchestration layer.</p></div> </article><article class="card sl-flex astro-jkgbziii"> <p class="title sl-flex astro-jkgbziii"> <svg aria-hidden="true" class="icon astro-jkgbziii astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.333em;"><path d="M18.71 7.21a1 1 0 0 0-1.42 0l-7.45 7.46-3.13-3.14A1.02 1.02 0 1 0 5.29 13l3.84 3.84a1.001 1.001 0 0 0 1.42 0l8.16-8.16a1 1 0 0 0 0-1.47Z"/></svg> <span class="astro-jkgbziii">Type-safe by design</span> </p> <div class="body astro-jkgbziii"><p>One inline-generic schema, three typed surfaces — events, conditions, actions. No <code dir="auto">as</code> casts, no <code dir="auto">.d.ts</code> files, no codegen.</p></div> </article><article class="card sl-flex astro-jkgbziii"> <p class="title sl-flex astro-jkgbziii"> <svg aria-hidden="true" class="icon astro-jkgbziii astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.333em;"></svg> <span class="astro-jkgbziii">Test without React</span> </p> <div class="body astro-jkgbziii"><p>Trigger handlers are nearly pure functions of a snapshot. Mock conditions, mock actions, fire events. RTL is optional.</p></div> </article><article class="card sl-flex astro-jkgbziii"> <p class="title sl-flex astro-jkgbziii"> <svg aria-hidden="true" class="icon astro-jkgbziii astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.333em;"><path d="M21.71 20.29 18 16.61A9 9 0 1 0 16.61 18l3.68 3.68a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.39ZM11 18a7 7 0 1 1 0-14 7 7 0 0 1 0 14Z"/></svg> <span class="astro-jkgbziii">DevTools built-in</span> </p> <div class="body astro-jkgbziii"><p>An inspector ring buffer ships in every runtime. Bridge to Redux DevTools, drop in <code dir="auto"><InspectorView></code>, or replay sessions.</p></div> </article><article class="card sl-flex astro-jkgbziii"> <p class="title sl-flex astro-jkgbziii"> <svg aria-hidden="true" class="icon astro-jkgbziii astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.333em;"><path d="M22 7.24a1 1 0 0 0-.29-.71l-4.24-4.24a1 1 0 0 0-1.1-.22 1 1 0 0 0-.32.22l-2.83 2.83L2.29 16.05a1 1 0 0 0-.29.71V21a1 1 0 0 0 1 1h4.24a1 1 0 0 0 .76-.29l10.87-10.93L21.71 8c.1-.1.17-.2.22-.33a1 1 0 0 0 0-.24v-.14l.07-.05ZM6.83 20H4v-2.83l9.93-9.93 2.83 2.83L6.83 20ZM18.17 8.66l-2.83-2.83 1.42-1.41 2.82 2.82-1.41 1.42Z"/></svg> <span class="astro-jkgbziii">ESLint + codemods</span> </p> <div class="body astro-jkgbziii"><p>8 lint rules catch the common mistakes. Codemods migrate from <code dir="auto">useEffect</code>, RTK listenerMiddleware, and (1.0) Redux Saga.</p></div> </article></div>
<div class="sl-heading-wrapper level-h2"><h2 id="try-it">Try it</h2><a class="sl-anchor-link" href="#try-it"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Try it”</span></a></div>
<div class="pm-tabs" data-pm-tabs> <script>
(() => {
class StarlightTabsRestore extends HTMLElement {
connectedCallback() {
const starlightTabs = this.closest('starlight-tabs');
if (!(starlightTabs instanceof HTMLElement) || typeof localStorage === 'undefined') return;
const syncKey = starlightTabs.dataset.syncKey;
if (!syncKey) return;
const label = localStorage.getItem(`starlight-synced-tabs__${syncKey}`);
if (!label) return;
const tabs = [...starlightTabs?.querySelectorAll('[role="tab"]')];
const tabIndexToRestore = tabs.findIndex(
(tab) => tab instanceof HTMLAnchorElement && tab.textContent?.trim() === label
);
const panels = starlightTabs?.querySelectorAll(':scope > [role="tabpanel"]');
const newTab = tabs[tabIndexToRestore];
const newPanel = panels[tabIndexToRestore];
if (tabIndexToRestore < 1 || !newTab || !newPanel) return;
tabs[0]?.setAttribute('aria-selected', 'false');
tabs[0]?.setAttribute('tabindex', '-1');
panels?.[0]?.setAttribute('hidden', 'true');
newTab.removeAttribute('tabindex');
newTab.setAttribute('aria-selected', 'true');
newPanel.removeAttribute('hidden');
}
}
customElements.define('starlight-tabs-restore', StarlightTabsRestore);
})()
</script><starlight-tabs data-sync-key="pm" class="astro-sk4c6idd"> <div class="tablist-wrapper not-content astro-sk4c6idd"> <ul role="tablist" class="astro-sk4c6idd"> <li role="presentation" class="tab astro-sk4c6idd"> <a role="tab" href="#tab-panel-49" id="tab-49" aria-selected="true" tabindex="0" class="astro-sk4c6idd"> pnpm </a> </li><li role="presentation" class="tab astro-sk4c6idd"> <a role="tab" href="#tab-panel-50" id="tab-50" aria-selected="false" tabindex="-1" class="astro-sk4c6idd"> npm </a> </li><li role="presentation" class="tab astro-sk4c6idd"> <a role="tab" href="#tab-panel-51" id="tab-51" aria-selected="false" tabindex="-1" class="astro-sk4c6idd"> yarn </a> </li><li role="presentation" class="tab astro-sk4c6idd"> <a role="tab" href="#tab-panel-52" id="tab-52" aria-selected="false" tabindex="-1" class="astro-sk4c6idd"> bun </a> </li> </ul> </div> <div id="tab-panel-49" aria-labelledby="tab-49" role="tabpanel"> <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" frame="terminal" data-language="sh"><code><span class="line"><span style="color:#B392F0">pnpm</span><span style="color:#9ECBFF"> add</span><span style="color:#9ECBFF"> @triggery/core</span><span style="color:#9ECBFF"> @triggery/react</span></span></code></pre> </div> <div id="tab-panel-50" aria-labelledby="tab-50" role="tabpanel" hidden> <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" frame="terminal" data-language="sh"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> @triggery/core</span><span style="color:#9ECBFF"> @triggery/react</span></span></code></pre> </div> <div id="tab-panel-51" aria-labelledby="tab-51" role="tabpanel" hidden> <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" frame="terminal" data-language="sh"><code><span class="line"><span style="color:#B392F0">yarn</span><span style="color:#9ECBFF"> add</span><span style="color:#9ECBFF"> @triggery/core</span><span style="color:#9ECBFF"> @triggery/react</span></span></code></pre> </div> <div id="tab-panel-52" aria-labelledby="tab-52" role="tabpanel" hidden> <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" frame="terminal" data-language="sh"><code><span class="line"><span style="color:#B392F0">bun</span><span style="color:#9ECBFF"> add</span><span style="color:#9ECBFF"> @triggery/core</span><span style="color:#9ECBFF"> @triggery/react</span></span></code></pre> </div> <starlight-tabs-restore class="astro-sk4c6idd"></starlight-tabs-restore> </starlight-tabs> <script type="module">class r extends HTMLElement{static#e=new Map;#t;#n="starlight-synced-tabs__";constructor(){super();const t=this.querySelector('[role="tablist"]');if(this.tabs=[...t.querySelectorAll('[role="tab"]')],this.panels=[...this.querySelectorAll(':scope > [role="tabpanel"]')],this.#t=this.dataset.syncKey,this.#t){const i=r.#e.get(this.#t)??[];i.push(this),r.#e.set(this.#t,i)}this.tabs.forEach((i,c)=>{i.addEventListener("click",e=>{e.preventDefault();const n=t.querySelector('[aria-selected="true"]');e.currentTarget!==n&&this.switchTab(e.currentTarget,c)}),i.addEventListener("keydown",e=>{const n=this.tabs.indexOf(e.currentTarget),s=e.key==="ArrowLeft"?n-1:e.key==="ArrowRight"?n+1:e.key==="Home"?0:e.key==="End"?this.tabs.length-1:null;s!==null&&this.tabs[s]&&(e.preventDefault(),this.switchTab(this.tabs[s],s))})})}switchTab(t,i,c=!0){if(!t)return;const e=c?this.getBoundingClientRect().top:0;this.tabs.forEach(s=>{s.setAttribute("aria-selected","false"),s.setAttribute("tabindex","-1")}),this.panels.forEach(s=>{s.hidden=!0});const n=this.panels[i];n&&(n.hidden=!1),t.removeAttribute("tabindex"),t.setAttribute("aria-selected","true"),c&&(t.focus(),r.#r(this,t),window.scrollTo({top:window.scrollY+(this.getBoundingClientRect().top-e),behavior:"instant"}))}#i(t){!this.#t||typeof localStorage>"u"||localStorage.setItem(this.#n+this.#t,t)}static#r(t,i){const c=t.#t,e=r.#s(i);if(!c||!e)return;const n=r.#e.get(c);if(n){for(const s of n){if(s===t)continue;const a=s.tabs.findIndex(o=>r.#s(o)===e);a!==-1&&s.switchTab(s.tabs[a],a,!1)}t.#i(e)}}static#s(t){return t.textContent?.trim()}}customElements.define("starlight-tabs",r);</script> </div> <script>(function(){const ICONS = {"pnpm":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"#F69220\" aria-hidden=\"true\" style=\"flex-shrink:0;\">\u003cpath d=\"M0 0v7.5h7.5V0H0Zm8.25 0v7.5h7.498V0H8.25Zm8.25 0v7.5H24V0h-7.5ZM8.25 8.25v7.5h7.498v-7.5H8.25Zm8.25 0v7.5H24v-7.5h-7.5ZM0 16.5V24h7.5v-7.5H0Zm8.25 0V24h7.498v-7.5H8.25Zm8.25 0V24H24v-7.5h-7.5Z\"/>\u003c/svg>","npm":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"#CB3837\" aria-hidden=\"true\" style=\"flex-shrink:0;\">\u003cpath d=\"M24 7.296L0 7.296L0 15.296L6.816 15.296L6.816 16.704L12.096 16.704L12.096 15.392L24 15.392L24 7.296ZM6.592 8.704L6.592 13.984L5.312 13.984L5.312 10.112L4 10.112L4 13.984L1.312 13.984L1.312 8.704L6.592 8.704ZM13.184 13.984L13.216 13.984L10.496 13.984L10.496 15.392L7.808 15.392L7.808 8.800L13.088 8.800Q13.216 10.400 13.184 13.984L13.184 13.984ZM22.592 8.704L22.592 13.984L21.312 13.984L21.312 10.112L20 10.112L20 13.984L18.592 13.984L18.592 10.112L17.312 10.112L17.312 13.984L14.592 13.984L14.592 8.704L22.592 8.704ZM11.904 12.704L11.904 10.112L10.592 10.112L10.592 12.704L11.904 12.704Z\"/>\u003c/svg>","yarn":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"#2C8EBB\" aria-hidden=\"true\" style=\"flex-shrink:0;\">\u003cpath d=\"M6.729 21.545Q6.393 21.377 6.225 21.041Q6.099 20.915 6.078 20.915Q6.057 20.915 5.973 21.041Q5.889 21.167 5.826 21.419Q5.763 21.671 5.679 21.839Q5.385 22.805 4.839 23.141Q4.293 23.477 3.327 23.267Q3.075 23.267 2.529 23.015Q1.731 22.595 2.151 21.839Q2.151 21.755 2.214 21.629Q2.277 21.503 2.277 21.419Q1.563 21.419 1.353 20.789Q0.849 19.445 1.017 18.416Q1.185 17.387 2.151 16.421L2.235 16.253Q2.403 15.959 2.403 15.791Q2.403 14.363 2.697 13.313Q3.033 12.053 3.831 11.045Q4.503 10.163 5.427 9.617Q5.595 9.533 5.616 9.407Q5.637 9.281 5.553 9.071Q4.839 8.189 4.629 6.971Q4.545 6.635 4.671 6.215Q4.755 5.921 5.007 5.417L5.175 5.039Q5.427 4.745 5.553 4.745Q5.931 4.661 6.561 4.199L6.855 3.989Q8.157 2.645 10.131 2.645Q10.341 2.645 10.446 2.582Q10.551 2.519 10.551 2.393Q10.719 1.595 11.307 0.839L11.727 0.419Q11.937 0.209 12.168 0.230Q12.399 0.251 12.525 0.545Q12.777 1.007 13.155 1.847L13.407 2.393Q13.617 2.729 13.827 2.519Q14.331 2.309 14.499 2.288Q14.667 2.267 14.751 2.393Q14.835 2.519 15.003 3.065Q16.011 7.265 13.701 10.919Q13.617 11.045 13.428 11.318Q13.239 11.591 13.155 11.759Q13.071 11.927 13.092 12.053Q13.113 12.179 13.281 12.389Q14.163 13.145 14.751 14.195Q15.339 15.245 15.507 16.421Q15.717 17.891 15.507 19.319Q15.423 19.697 15.507 19.760Q15.591 19.823 15.927 19.739Q17.397 19.277 18.531 18.521L18.867 18.353Q19.623 17.891 20.043 17.723Q20.673 17.429 21.303 17.345L21.681 17.345Q22.101 17.261 22.374 17.366Q22.647 17.471 22.836 17.723Q23.025 17.975 23.025 18.269Q23.025 18.857 22.353 19.067Q20.631 19.403 18.804 20.726Q16.977 22.049 14.457 22.721Q14.373 22.721 14.205 22.805Q14.037 22.889 13.953 23.015Q13.659 23.225 13.323 23.309Q13.113 23.393 12.651 23.435L12.231 23.519L11.895 23.561Q9.375 23.771 8.031 23.771Q7.275 23.771 6.729 23.645Q5.973 23.393 5.805 22.889Q5.595 22.091 6.225 21.671Q6.351 21.671 6.519 21.608Q6.687 21.545 6.729 21.545Z\"/>\u003c/svg>","bun":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"#FBF0DF\" aria-hidden=\"true\" style=\"flex-shrink:0;\" data-pm-icon=\"bun\">\u003cpath d=\"M11.966 22.566c6.609 0 11.966-4.326 11.966-9.661 0-3.308-2.051-6.23-5.204-7.963-1.283-.713-2.291-1.353-3.13-1.885-1.58-1.004-2.555-1.623-3.632-1.623-1.094 0-2.327.783-3.955 1.816a49.78 49.78 0 0 1-2.808 1.692C2.051 6.675 0 9.597 0 12.905c0 5.335 5.357 9.66 11.966 9.66Zm-1.397-17.83a5.885 5.885 0 0 0 .497-2.403c0-.144.201-.186.229-.028.656 2.775-.9 4.15-2.051 4.61-.124.048-.199-.12-.103-.208a5.748 5.748 0 0 0 1.428-1.971Zm2.052-.102a5.795 5.795 0 0 0-.78-2.3v-.015c-.068-.123.086-.263.185-.172 1.956 2.105 1.303 4.055.554 5.037-.082.102-.229-.003-.188-.126a5.837 5.837 0 0 0 .229-2.424Zm1.771-.559a5.708 5.708 0 0 0-1.607-1.801V2.26c-.112-.085-.024-.274.113-.218 2.588 1.084 2.766 3.171 2.452 4.395a.116.116 0 0 1-.048.071.11.11 0 0 1-.153-.026.118.118 0 0 1-.022-.083 5.864 5.864 0 0 0-.735-2.324Zm-5.072.559c-.616.544-1.279.758-2.058.997-.116 0-.194-.078-.155-.18 1.747-.907 2.369-1.645 2.99-2.771 0 0 .155-.117.188.085 0 .303-.348 1.325-.965 1.869Zm4.931 11.205a2.949 2.949 0 0 1-.935 1.549 2.16 2.16 0 0 1-1.282.618 2.167 2.167 0 0 1-1.323-.618 2.95 2.95 0 0 1-.923-1.549.243.243 0 0 1 .064-.197.23.23 0 0 1 .192-.069h3.954a.226.226 0 0 1 .19.07.239.239 0 0 1 .063.196Zm-5.443-2.17a1.85 1.85 0 0 1-2.377-.244 1.969 1.969 0 0 1-.233-2.44c.207-.318.502-.565.846-.711a1.84 1.84 0 0 1 1.089-.11c.365.075.701.26.964.53.264.27.443.616.515.99a1.98 1.98 0 0 1-.108 1.118 1.923 1.923 0 0 1-.696.866Zm8.471.005a1.849 1.849 0 0 1-2.374-.252 1.956 1.956 0 0 1-.546-1.362c0-.383.11-.758.319-1.076.207-.318.502-.566.847-.711a1.84 1.84 0 0 1 1.09-.108c.366.076.702.261.965.533s.44.617.512.993a1.98 1.98 0 0 1-.113 1.118 1.922 1.922 0 0 1-.7.865Z\"/>\u003c/svg>"};
// See FrameworkTabs.astro for why this fallback exists — Starlight's own
// `starlight-tabs` custom-element registration sometimes doesn't make it
// into the production bundle, leaving the element with no click handlers.
// Registration is deferred to `DOMContentLoaded` so every `<starlight-tabs>`
// is fully parsed (with its tablist and tabpanels) before the upgrade fires.
function defineStarlightTabs() {
if (typeof customElements === 'undefined') return;
if (customElements.get('starlight-tabs')) return;
const STORAGE_PREFIX = 'starlight-synced-tabs__';
class StarlightTabs extends HTMLElement {
static syncedTabs = new Map();
constructor() {
super();
const tablist = this.querySelector('[role="tablist"]');
if (!tablist) return;
this.tabs = Array.from(tablist.querySelectorAll('[role="tab"]'));
this.panels = Array.from(this.querySelectorAll(':scope > [role="tabpanel"]'));
this.syncKey = this.dataset.syncKey;
if (this.syncKey) {
const arr = StarlightTabs.syncedTabs.get(this.syncKey) || [];
arr.push(this);
StarlightTabs.syncedTabs.set(this.syncKey, arr);
}
this.tabs.forEach((tab, i) => {
tab.addEventListener('click', (e) => {
e.preventDefault();
const sel = tablist.querySelector('[aria-selected="true"]');
if (e.currentTarget !== sel) this.switchTab(e.currentTarget, i);
});
tab.addEventListener('keydown', (e) => {
const idx = this.tabs.indexOf(e.currentTarget);
const next =
e.key === 'ArrowLeft' ? idx - 1
: e.key === 'ArrowRight' ? idx + 1
: e.key === 'Home' ? 0
: e.key === 'End' ? this.tabs.length - 1
: null;
if (next === null || !this.tabs[next]) return;
e.preventDefault();
this.switchTab(this.tabs[next], next);
});
});
}
switchTab(newTab, idx, shouldSync) {
if (shouldSync === undefined) shouldSync = true;
const top = shouldSync ? this.getBoundingClientRect().top : 0;
this.tabs.forEach((t) => {
t.setAttribute('aria-selected', 'false');
t.setAttribute('tabindex', '-1');
});
this.panels.forEach((p) => (p.hidden = true));
if (this.panels[idx]) this.panels[idx].hidden = false;
newTab.removeAttribute('tabindex');
newTab.setAttribute('aria-selected', 'true');
if (!shouldSync) return;
newTab.focus();
const label = (newTab.textContent || '').trim();
if (this.syncKey && typeof localStorage !== 'undefined') {
try {
localStorage.setItem(STORAGE_PREFIX + this.syncKey, label);
} catch (e) {}
}
const peers = StarlightTabs.syncedTabs.get(this.syncKey) || [];
for (let p = 0; p < peers.length; p++) {
const peer = peers[p];
if (peer === this) continue;
const j = peer.tabs.findIndex((t) => (t.textContent || '').trim() === label);
if (j >= 0) peer.switchTab(peer.tabs[j], j, false);
}
window.scrollTo({
top: window.scrollY + (this.getBoundingClientRect().top - top),
behavior: 'instant',
});
}
}
customElements.define('starlight-tabs', StarlightTabs);
if (!customElements.get('starlight-tabs-restore')) {
customElements.define(
'starlight-tabs-restore',
class extends HTMLElement {
connectedCallback() {
const tabs = this.closest('starlight-tabs');
if (!tabs || typeof localStorage === 'undefined') return;
const key = tabs.dataset.syncKey;
if (!key) return;
const label = localStorage.getItem(STORAGE_PREFIX + key);
if (!label) return;
const list = Array.from(tabs.querySelectorAll('[role="tab"]'));
const idx = list.findIndex((t) => (t.textContent || '').trim() === label);
if (idx < 1) return;
const panels = tabs.querySelectorAll(':scope > [role="tabpanel"]');
list[0] && list[0].setAttribute('aria-selected', 'false');
list[0] && list[0].setAttribute('tabindex', '-1');
panels[0] && panels[0].setAttribute('hidden', 'true');
list[idx] && list[idx].removeAttribute('tabindex');
list[idx] && list[idx].setAttribute('aria-selected', 'true');
panels[idx] && panels[idx].removeAttribute('hidden');
}
},
);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', defineStarlightTabs, { once: true });
} else {
defineStarlightTabs();
}
(() => {
function decorate(root) {
root.querySelectorAll('.pm-tabs[data-pm-tabs] [role="tab"]').forEach((tab) => {
if (tab.dataset.pmIconified === '1') return;
const label = (tab.textContent || '').trim();
const svg = ICONS[label];
if (!svg) return;
const wrap = document.createElement('span');
wrap.style.display = 'inline-flex';
wrap.style.width = '1em';
wrap.style.height = '1em';
wrap.style.flexShrink = '0';
wrap.style.marginRight = '0.5em';
wrap.style.pointerEvents = 'none';
wrap.innerHTML = svg;
tab.insertBefore(wrap, tab.firstChild);
tab.dataset.pmIconified = '1';
});
}
const run = () => decorate(document);
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', run);
} else {
run();
}
document.addEventListener('astro:page-load', run);
})();
})();</script>
<p>For SolidJS use <code dir="auto">@triggery/solid</code>, for Vue 3 use <code dir="auto">@triggery/vue</code> — the API is identical.</p>
<a class="sl-link-button not-content primary astro-i25ua3fl" href="/guide/getting-started/"> Read the Getting Started guide <svg aria-hidden="true" class="astro-i25ua3fl astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z"/></svg> </a>
<a class="sl-link-button not-content minimal astro-i25ua3fl" href="/recipes/react/notification-pipeline/"> See it in a real recipe </a>
<a class="sl-link-button not-content minimal astro-i25ua3fl" href="https://stackblitz.com/github/triggeryjs/triggery/tree/main/examples/vite-react-counter?file=README.md"> Open in StackBlitz <svg aria-hidden="true" class="astro-i25ua3fl astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M19.33 10.18a1 1 0 0 1-.77 0 1 1 0 0 1-.62-.93l.01-1.83-8.2 8.2a1 1 0 0 1-1.41-1.42l8.2-8.2H14.7a1 1 0 0 1 0-2h4.25a1 1 0 0 1 1 1v4.25a1 1 0 0 1-.62.93Z"/><path d="M11 4a1 1 0 1 1 0 2H7a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-4a1 1 0 1 1 2 0v4a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h4Z"/></svg> </a>
<a class="sl-link-button not-content minimal astro-i25ua3fl" href="https://codesandbox.io/p/github/triggeryjs/triggery/main/examples/vite-react-counter"> Open in CodeSandbox <svg aria-hidden="true" class="astro-i25ua3fl astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M19.33 10.18a1 1 0 0 1-.77 0 1 1 0 0 1-.62-.93l.01-1.83-8.2 8.2a1 1 0 0 1-1.41-1.42l8.2-8.2H14.7a1 1 0 0 1 0-2h4.25a1 1 0 0 1 1 1v4.25a1 1 0 0 1-.62.93Z"/><path d="M11 4a1 1 0 1 1 0 2H7a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-4a1 1 0 1 1 2 0v4a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h4Z"/></svg> </a>
<div class="sl-heading-wrapper level-h2"><h2 id="what-people-are-saying">What people are saying</h2><a class="sl-anchor-link" href="#what-people-are-saying"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “What people are saying”</span></a></div>
<blockquote>
<p>Triggery is the only orchestration library where the same scenario file makes sense to a junior engineer on Monday and a senior engineer reviewing it on Friday.</p>
<p>— early adopter, 2026</p>
</blockquote>
<div class="sl-heading-wrapper level-h2"><h2 id="compare-with-familiar-tools">Compare with familiar tools</h2><a class="sl-anchor-link" href="#compare-with-familiar-tools"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Compare with familiar tools”</span></a></div>
<table><thead><tr><th></th><th>Triggery</th><th>Redux Saga</th><th>RTK listenerMiddleware</th><th>XState</th><th>RxJS</th></tr></thead><tbody><tr><td><strong>Hook-first API</strong></td><td>yes</td><td>no</td><td>partial</td><td>yes</td><td>no</td></tr><tr><td><strong>Per-scenario file</strong></td><td>yes</td><td>no</td><td>partial</td><td>yes</td><td>no</td></tr><tr><td><strong>Cross-framework</strong></td><td>yes</td><td>Redux-only</td><td>Redux-only</td><td>yes</td><td>yes</td></tr><tr><td><strong>Inline-generic schema</strong></td><td>yes</td><td>no</td><td>no</td><td>yes</td><td>n/a</td></tr><tr><td><strong>Built-in DevTools</strong></td><td>yes</td><td>external</td><td>RTK DevTools</td><td>yes</td><td>external</td></tr><tr><td><strong>Bundle (core, gzip)</strong></td><td>< 6kB</td><td>~17kB</td><td>bundled w/ RTK</td><td>~16kB</td><td>~46kB</td></tr></tbody></table>
<p>For longer-form comparisons see <a href="/migration/">Migration → Comparison</a>.</p>
<div class="sl-heading-wrapper level-h2"><h2 id="backed-by-a-healthy-roadmap">Backed by a healthy roadmap</h2><a class="sl-anchor-link" href="#backed-by-a-healthy-roadmap"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Backed by a healthy roadmap”</span></a></div>
<p>Triggery is at the 0.9.x release-candidate window — feature freeze, last round of community testing before 1.0. The runtime has been used in production for orchestration since early 2026 and the test coverage targets are enforced in CI (lines ≥ 95%, branches ≥ 85%). See the <a href="https://github.com/triggeryjs/triggery/blob/main/ROADMAP.md">public roadmap</a> for what’s next.</p>
<a class="sl-link-button not-content secondary astro-i25ua3fl" href="https://github.com/sponsors/awesomesk1ll"> Sponsor the project <svg aria-hidden="true" class="astro-i25ua3fl astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M20.16 5A6.29 6.29 0 0 0 12 4.36a6.27 6.27 0 0 0-8.16 9.48l6.21 6.22a2.78 2.78 0 0 0 3.9 0l6.21-6.22a6.27 6.27 0 0 0 0-8.84m-1.41 7.46-6.21 6.21a.76.76 0 0 1-1.08 0l-6.21-6.24a4.29 4.29 0 0 1 0-6 4.27 4.27 0 0 1 6 0 1 1 0 0 0 1.42 0 4.27 4.27 0 0 1 6 0 4.29 4.29 0 0 1 .08 6Z"/></svg> </a> </div> <script type="module">const n='<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></g></svg>',s='<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 6L9 17l-5-5"/></svg>';class i extends HTMLElement{constructor(){super()}connectedCallback(){const e=()=>{const o=t==="ready"?n:s;this.innerHTML='<span class="w-full h-full block [&_svg]:w-full [&_svg]:h-full">'+o+"</span>"};let t="ready";e(),this.addEventListener("click",()=>{if(t==="ready"){t="success",e();const o=this.dataset.code||"";navigator.clipboard.writeText(o),setTimeout(()=>{t="ready",e()},1500)}})}}customElements.define("nova-code-copy-button",i);</script> <script type="module" src="/_astro/MarkdownContent.astro_astro_type_script_index_0_lang.nECfJVFg.js"></script> <footer class="sl-flex astro-hn72iikk"> <div class="meta sl-flex astro-hn72iikk"> <a href="https://github.com/triggeryjs/triggery/edit/main/apps/docs/src/content/docs/index.mdx" class="sl-flex print:hidden astro-ms2oaep6"><svg aria-hidden="true" class="astro-ms2oaep6 astro-hp7mp5ch" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.2em;"><path d="M22 7.24a1 1 0 0 0-.29-.71l-4.24-4.24a1 1 0 0 0-1.1-.22 1 1 0 0 0-.32.22l-2.83 2.83L2.29 16.05a1 1 0 0 0-.29.71V21a1 1 0 0 0 1 1h4.24a1 1 0 0 0 .76-.29l10.87-10.93L21.71 8c.1-.1.17-.2.22-.33a1 1 0 0 0 0-.24v-.14l.07-.05ZM6.83 20H4v-2.83l9.93-9.93 2.83 2.83L6.83 20ZM18.17 8.66l-2.83-2.83 1.42-1.41 2.82 2.82-1.41 1.42Z"/></svg>Edit page</a> <p>Last updated: <time datetime="2026-05-26T00:33:04.000Z">May 26, 2026</time></p> </div> <div class="flex min-w-full flex-row items-stretch justify-between gap-2 px-1 pt-0 pb-6 print:hidden" dir="ltr"> <div class="flex-1"></div> </div> </footer> </div> </div> </main> </div> </div> </div> </div> </body></html>