Repository navigation
Expand file tree
/
Copy pathgetting-started.html
More file actions
253 lines (251 loc) · 93 KB
/
Copy pathgetting-started.html
File metadata and controls
253 lines (251 loc) · 93 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
<!DOCTYPE html>
<html lang="en-US" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Getting Started | Logos DX</title>
<meta name="description" content="Focused TypeScript utilities for building JS apps in any runtime">
<meta name="generator" content="VitePress v2.0.0-alpha.16">
<link rel="preload stylesheet" href="/assets/style.puAb6RVC.css" as="style">
<link rel="preload stylesheet" href="/vp-icons.css" as="style">
<script type="module" src="/assets/app.Bp0I8K6j.js"></script>
<link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="modulepreload" href="/assets/chunks/framework.BVGSIcOn.js">
<link rel="modulepreload" href="/assets/chunks/theme.DIao9Iec.js">
<link rel="modulepreload" href="/assets/getting-started.md.B-U1EswA.lean.js">
<link rel="icon" href="/images/app-icon-red.png">
<link rel="icon" href="/images/app-icon-white.png" media="(prefers-color-scheme: dark)">
<meta property="og:title" content="Logos DX">
<meta property="og:description" content="Focused TypeScript utilities for building JS apps in any runtime">
<meta property="og:image" content="https://logosdx.dev/images/screenshot-site.png">
<meta property="og:url" content="https://logosdx.dev">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Logos DX">
<meta name="twitter:description" content="Focused TypeScript utilities for building JS apps in any runtime">
<meta name="twitter:image" content="https://logosdx.dev/images/screenshot-site.png">
<link rel="mask-icon" href="/images/app-icon-red.png" color="#ffffff">
<link rel="apple-touch-icon" href="/images/app-icon-red.png" sizes="180x180">
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H547DPM1VY"></script>
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date),gtag("config","G-H547DPM1VY");</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@7.0.0/css/fontawesome.min.css">
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@7.0.0/js/all.min.js"></script>
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
</head>
<body>
<div id="app"><div class="Layout" data-v-f534aa51><!--[--><!--]--><!--[--><span tabindex="-1" data-v-640e4194></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-640e4194>Skip to content</a><!--]--><!----><header class="VPNav" data-v-f534aa51 data-v-6bbcf939><div class="VPNavBar has-sidebar top" data-v-6bbcf939 data-v-7bf307cf><div class="wrapper" data-v-7bf307cf><div class="container" data-v-7bf307cf><div class="title" data-v-7bf307cf><div class="VPNavBarTitle has-sidebar" data-v-7bf307cf data-v-4f7b1d18><a class="title" href="/" data-v-4f7b1d18><!--[--><!--]--><!--[--><img class="VPImage logo" src="/images/app-icon-red.png" alt data-v-056bc521><!--]--><span data-v-4f7b1d18>Logos DX</span><!--[--><!--]--></a></div></div><div class="content" data-v-7bf307cf><div class="content-body" data-v-7bf307cf><!--[--><!--]--><div class="VPNavBarSearch search" data-v-7bf307cf data-v-bff53c42><!--[--><button type="button" class="VPNavBarSearchButton" aria-label="Search" aria-keyshortcuts="/ control+k meta+k" data-v-bff53c42 data-v-2484d85f><span class="vpi-search" aria-hidden="true" data-v-2484d85f></span><span class="text" data-v-2484d85f>Search</span><span class="keys" aria-hidden="true" data-v-2484d85f><kbd class="key-cmd" data-v-2484d85f>⌘</kbd><kbd class="key-ctrl" data-v-2484d85f>Ctrl</kbd><kbd data-v-2484d85f>K</kbd></span></button><!----><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-7bf307cf data-v-961a7814><span id="main-nav-aria-label" class="visually-hidden" data-v-961a7814> Main Navigation </span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/" tabindex="0" data-v-961a7814 data-v-2320b7bd><!--[--><span data-v-2320b7bd>Home</span><!--]--></a><!--]--><!--[--><a class="VPLink link vp-external-link-icon VPNavBarMenuLink" href="https://typedoc.logosdx.dev" target="_blank" rel="noreferrer" tabindex="0" data-v-961a7814 data-v-2320b7bd><!--[--><span data-v-2320b7bd>TypeDocs</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/llms.txt" tabindex="0" data-v-961a7814 data-v-2320b7bd><!--[--><span data-v-2320b7bd>llms.txt</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-7bf307cf data-v-45c9087c><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-45c9087c data-v-ef09584f data-v-e40da30e><span class="check" data-v-e40da30e><span class="icon" data-v-e40da30e><!--[--><span class="vpi-sun sun" data-v-ef09584f></span><span class="vpi-moon moon" data-v-ef09584f></span><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-7bf307cf data-v-69de79bc data-v-31c6b9fb><!--[--><a class="VPSocialLink no-icon" href="https://github.com/logosdx/monorepo" aria-label="github" target="_blank" rel="me noopener" data-v-31c6b9fb data-v-6c2b535d><span class="vpi-social-github"></span></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-7bf307cf data-v-b9c7a6a4 data-v-5efd9d41><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-5efd9d41><span class="vpi-more-horizontal icon" data-v-5efd9d41></span></button><div class="menu" data-v-5efd9d41><div class="VPMenu" data-v-5efd9d41 data-v-5c38526d><!----><!--[--><!--[--><!----><div class="group" data-v-b9c7a6a4><div class="item appearance" data-v-b9c7a6a4><p class="label" data-v-b9c7a6a4>Appearance</p><div class="appearance-action" data-v-b9c7a6a4><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-b9c7a6a4 data-v-ef09584f data-v-e40da30e><span class="check" data-v-e40da30e><span class="icon" data-v-e40da30e><!--[--><span class="vpi-sun sun" data-v-ef09584f></span><span class="vpi-moon moon" data-v-ef09584f></span><!--]--></span></span></button></div></div></div><div class="group" data-v-b9c7a6a4><div class="item social-links" data-v-b9c7a6a4><div class="VPSocialLinks social-links-list" data-v-b9c7a6a4 data-v-31c6b9fb><!--[--><a class="VPSocialLink no-icon" href="https://github.com/logosdx/monorepo" aria-label="github" target="_blank" rel="me noopener" data-v-31c6b9fb data-v-6c2b535d><span class="vpi-social-github"></span></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-7bf307cf data-v-35e09ed1><span class="container" data-v-35e09ed1><span class="top" data-v-35e09ed1></span><span class="middle" data-v-35e09ed1></span><span class="bottom" data-v-35e09ed1></span></span></button></div></div></div></div><div class="divider" data-v-7bf307cf><div class="divider-line" data-v-7bf307cf></div></div></div><!----></header><div class="VPLocalNav has-sidebar empty" data-v-f534aa51 data-v-fc6cb30b><div class="container" data-v-fc6cb30b><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-fc6cb30b><span class="vpi-align-left menu-icon" data-v-fc6cb30b></span><span class="menu-text" data-v-fc6cb30b>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-fc6cb30b data-v-b3864467><button data-v-b3864467>Return to top</button><!----></div></div></div><aside class="VPSidebar" data-v-f534aa51 data-v-dec53b96><div class="curtain" data-v-dec53b96></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-dec53b96><span class="visually-hidden" id="sidebar-aria-label" data-v-dec53b96> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="no-transition group" data-v-2fe2f2c8><section class="VPSidebarItem level-0 has-active" data-v-2fe2f2c8 data-v-3052c330><div class="item" role="button" tabindex="0" data-v-3052c330><div class="indicator" data-v-3052c330></div><h2 class="text" data-v-3052c330>Introduction</h2><!----></div><div class="items" data-v-3052c330><!--[--><div class="VPSidebarItem level-1 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/getting-started.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Getting Started</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/what-is-logosdx.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>What is LogosDX?</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-2fe2f2c8><section class="VPSidebarItem level-0" data-v-2fe2f2c8 data-v-3052c330><div class="item" role="button" tabindex="0" data-v-3052c330><div class="indicator" data-v-3052c330></div><h2 class="text" data-v-3052c330>Packages</h2><!----></div><div class="items" data-v-3052c330><!--[--><section class="VPSidebarItem level-1 collapsible collapsed is-link" data-v-3052c330 data-v-3052c330><div class="item" tabindex="0" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/observer/" data-v-3052c330><!--[--><h3 class="text" data-v-3052c330>Observer</h3><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-3052c330><span class="vpi-chevron-right caret-icon" data-v-3052c330></span></div></div><div class="items" data-v-3052c330><!--[--><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/observer/events.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Events</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/observer/generators.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Generators</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/observer/queues.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Queues</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/observer/relay.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Relay</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/observer/advanced.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Advanced</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed is-link" data-v-3052c330 data-v-3052c330><div class="item" tabindex="0" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/utils/" data-v-3052c330><!--[--><h3 class="text" data-v-3052c330>Utils</h3><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-3052c330><span class="vpi-chevron-right caret-icon" data-v-3052c330></span></div></div><div class="items" data-v-3052c330><!--[--><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/utils/error-handling.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Error Handling</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/utils/flow-control.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Flow Control</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/utils/data.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Data Operations</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/utils/performance.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Performance & Caching</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/utils/validation.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Validation & Type Guards</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed is-link" data-v-3052c330 data-v-3052c330><div class="item" tabindex="0" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/fetch/" data-v-3052c330><!--[--><h3 class="text" data-v-3052c330>Fetch</h3><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-3052c330><span class="vpi-chevron-right caret-icon" data-v-3052c330></span></div></div><div class="items" data-v-3052c330><!--[--><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/fetch/configuration.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Configuration</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/fetch/requests.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Making Requests</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/fetch/hooks.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Hooks</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/fetch/plugins.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Plugins</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/fetch/resilience.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Resilience</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/fetch/policies.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Policies</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/fetch/events.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Events</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/fetch/advanced.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Advanced</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed is-link" data-v-3052c330 data-v-3052c330><div class="item" tabindex="0" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/state-machine/" data-v-3052c330><!--[--><h3 class="text" data-v-3052c330>State Machine</h3><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-3052c330><span class="vpi-chevron-right caret-icon" data-v-3052c330></span></div></div><div class="items" data-v-3052c330><!--[--><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/state-machine/api.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>API Reference</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/state-machine/guide.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Practical Guide</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed is-link" data-v-3052c330 data-v-3052c330><div class="item" tabindex="0" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/dom/" data-v-3052c330><!--[--><h3 class="text" data-v-3052c330>Dom</h3><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-3052c330><span class="vpi-chevron-right caret-icon" data-v-3052c330></span></div></div><div class="items" data-v-3052c330><!--[--><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/dom/selection.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Selection</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/dom/styling.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Styling</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/dom/aria.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Aria</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/dom/events.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Events</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/dom/templates.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Templates</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/dom/animate.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Animate</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/dom/observers.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Observers</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed is-link" data-v-3052c330 data-v-3052c330><div class="item" tabindex="0" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/storage/" data-v-3052c330><!--[--><h3 class="text" data-v-3052c330>Storage</h3><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-3052c330><span class="vpi-chevron-right caret-icon" data-v-3052c330></span></div></div><div class="items" data-v-3052c330><!--[--><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/storage/api.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>API Reference</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/storage/drivers.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Drivers</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/storage/events.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Events</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed is-link" data-v-3052c330 data-v-3052c330><div class="item" tabindex="0" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/localize/" data-v-3052c330><!--[--><h3 class="text" data-v-3052c330>Localize</h3><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-3052c330><span class="vpi-chevron-right caret-icon" data-v-3052c330></span></div></div><div class="items" data-v-3052c330><!--[--><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/localize/translations.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Translations</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/localize/pluralization.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Pluralization</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/localize/intl.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Intl Formatting</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/localize/async-loading.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Async Loading</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/localize/namespaces.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Namespaces</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/localize/events.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Events</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/localize/type-extractor.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Type Extractor</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/localize/api.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>API Reference</p><!--]--></a><!----></div><!----></div><!--]--></div></section><div class="VPSidebarItem level-1 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/hooks.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Hooks</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/react.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>React</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-3052c330 data-v-3052c330><div class="item" data-v-3052c330><div class="indicator" data-v-3052c330></div><a class="VPLink link link" href="/packages/slides.html" data-v-3052c330><!--[--><p class="text" data-v-3052c330>Slides</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-f534aa51 data-v-f832591b><div class="VPDoc has-sidebar has-aside" data-v-f832591b data-v-d420190f><!--[--><!--]--><div class="container" data-v-d420190f><div class="aside" data-v-d420190f><div class="aside-curtain" data-v-d420190f></div><div class="aside-container" data-v-d420190f><div class="aside-content" data-v-d420190f><div class="VPDocAside" data-v-d420190f data-v-810bb596><!--[--><!--]--><!--[--><!--]--><nav aria-labelledby="doc-outline-aria-label" class="VPDocAsideOutline" data-v-810bb596 data-v-afd0f391><div class="content" data-v-afd0f391><div class="outline-marker" data-v-afd0f391></div><div aria-level="2" class="outline-title" id="doc-outline-aria-label" role="heading" data-v-afd0f391>On this page</div><ul class="VPDocOutlineItem root" data-v-afd0f391 data-v-c49c31c8><!--[--><!--]--></ul></div></nav><!--[--><!--]--><div class="spacer" data-v-810bb596></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-d420190f><div class="content-container" data-v-d420190f><!--[--><!--]--><main class="main" data-v-d420190f><div style="position:relative;" class="vp-doc _getting-started" data-v-d420190f><div><h1 id="getting-started" tabindex="-1">Getting Started <a class="header-anchor" href="#getting-started" aria-label="Permalink to “Getting Started”"></a></h1><p>In your project, install the packages you need.</p><div class="vp-code-group"><div class="tabs"><input type="radio" name="group-6" id="tab-7" checked><label data-title="npm" for="tab-7">npm</label><input type="radio" name="group-6" id="tab-8"><label data-title="yarn" for="tab-8">yarn</label><input type="radio" name="group-6" id="tab-9"><label data-title="pnpm" for="tab-9">pnpm</label></div><div class="blocks"><div class="language-bash active"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @logosdx/observer</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @logosdx/utils</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @logosdx/fetch</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...etc.</span></span></code></pre></div><div class="language-bash"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">yarn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> add</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @logosdx/observer</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @logosdx/utils</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @logosdx/fetch</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...etc.</span></span></code></pre></div><div class="language-bash"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> add</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @logosdx/observer</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @logosdx/utils</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @logosdx/fetch</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...etc.</span></span></code></pre></div></div></div><p>Or in your browser, use the CDN:</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"><</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D;">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> src</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">"https://cdn.jsdelivr.net/npm/@logosdx/observer@latest/dist/browser/bundle.min.js"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">></</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D;">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"><</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D;">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> src</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">"https://cdn.jsdelivr.net/npm/@logosdx/utils@latest/dist/browser/bundle.min.js"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">></</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D;">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"><</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D;">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> src</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">"https://cdn.jsdelivr.net/npm/@logosdx/fetch@latest/dist/browser/bundle.min.js"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">></</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D;">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"><!-- ...etc. --></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"><</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D;">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">ObserverEngine</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> LogosDX.Observer;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">composeFlow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">attempt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> LogosDX.Utils;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FetchEngine</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> LogosDX.Fetch;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"></</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D;">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">></span></span></code></pre></div><h2 id="usage" tabindex="-1">Usage <a class="header-anchor" href="#usage" aria-label="Permalink to “Usage”"></a></h2><p><strong>Normal observer patterns, but with type safety.</strong></p><div class="language-ts"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { ObserverEngine } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> '@logosdx/observer'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// Define your events like a civilized person</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> AppEvents</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'user:login'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">userId</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">token</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">timestamp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> };</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'user:logout'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">userId</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> };</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'user:login-failed'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">userId</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">error</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> Error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> };</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'system:ready'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> observer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> ObserverEngine</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"><</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">AppEvents</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">>();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// Listen and emit with full type safety</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'user:login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">timestamp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">`User ${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">userId</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">} logged in at ${</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> Date</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">(</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">timestamp</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">)</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">emit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'user:login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> userId: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'alice'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> token: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'alice-token'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> timestamp: Date.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">now</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">});</span></span></code></pre></div><p><strong>But what if you wanted to get weird?</strong></p><div class="language-ts"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> stopBeingNosy</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">^</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;">user:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">`🕵🏻♂️ ${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">data</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">userId</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">} is doing something`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, event, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'👀'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">emit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'user:login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> userId: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'alice'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> timestamp: Date.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">now</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// 🕵🏻♂️ alice is doing something user:login 👀</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'stop-being-nosy'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> stopBeingNosy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">());</span></span></code></pre></div><p><strong>Let's give these users something to actually log in to.</strong></p><div class="language-ts"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { FetchEngine } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> '@logosdx/fetch'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { attempt } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> '@logosdx/utils'</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// Your Fetch instance can have a state from which you can make</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// decisions about the request.</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> ApiState</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;"> authToken</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;"> userId</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// You can also define headers and query params</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// you will expect throughout your app.</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> ApiHeaders</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'X-User-ID'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> ApiQueryParams</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;"> page</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> api</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> FetchEngine</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"><</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ApiHeaders</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ApiQueryParams</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ApiState</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">>({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> baseUrl: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'https://rainbow-loans.com'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> retry: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> maxAttempts: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> retryableStatusCodes: [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">501</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">502</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">503</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">504</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> headers: {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'Content-Type'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'application/json'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'Accept'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'application/json'</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'user:login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">token</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // Once you have a token, you can set the state of the</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // Fetch instance to use it in the next request.</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> api.state.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">({ authToken: token, userId });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'user:logout'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // When the user logs out, you can clear the state of the</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // Fetch instance to avoid using the token in the next request.</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> api.state.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">({ authToken: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, userId: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> signIn</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">user</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">password</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // Go-style error handling, with type safety.</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">err</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> attempt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'/signin'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, { user, password }));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (err) {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // Transport failure — the exchange never completed</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">emit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'user:login-failed'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> userId: user,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> error: err</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> err;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">response.ok) {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // The server answered "no" — a 401 is an answer, not an exception.</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // Status and body are right here, nothing to dig out of an error.</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> rejected</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> Error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">`Sign-in rejected: ${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">response</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">status</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">emit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'user:login-failed'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> userId: user,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> error: rejected</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> rejected;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">token</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> response.data;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">emit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'user:login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> userId: user,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> token,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> timestamp: Date.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">now</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> user;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><p><strong>And, unfortunately for them, you use a really shitty payments API.</strong></p><div class="language-ts"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { composeFlow, attempt } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> '@logosdx/utils'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> painPal</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> FetchEngine</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> baseUrl: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'https://painpal.com'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> retry: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> maxAttempts: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> retryableStatusCodes: [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">429</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">501</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">502</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">503</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">504</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> headers: {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'Content-Type'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'application/json'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'Accept'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'application/json'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'X-API-Key'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'painpal-api-key'</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> _makePayment</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">paymentToken</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">amount</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">err</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> attempt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> painPal.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'/payments'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, { paymentToken, amount }));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (err) {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // Transport failure — the exchange never completed</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">emit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'payment:failed'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> paymentToken,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> amount,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> error: err</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> err;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">response.ok) {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // A declined payment resolves — it only counts as a failure if</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // you say so. The throw below is also what feeds the circuit</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // breaker wrapping this function: breakers count throws, and a</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // non-2xx response no longer throws on its own.</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> declined</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> Error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">`PainPal said no: ${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">response</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">status</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">emit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'payment:failed'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> paymentToken,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> amount,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> error: declined</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> declined;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> observer.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">emit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'payment:success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> paymentToken,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> amount,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> timestamp: Date.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">now</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> response.data;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// This makePayment function is now rate-limited to 10</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// calls per second, and has a circuit breaker that trips</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// after 3 failures.</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> makePayment</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> composeFlow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> _makePayment,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> circuitBreaker: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> maxFailures: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> resetAfter: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> onTripped</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'PainPal is being sensitive again'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, error);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> rateLimit: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> maxCalls: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> windowMs: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> onLimitReached</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">nextAvailable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'We might be over-doing it...'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, error);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">'Try again at'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, nextAvailable);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span></code></pre></div><p><strong>And you're done!</strong></p><p>You can deploy to production and be confident that if your users complain, it will be because of PainPal, not because you don't have the right abstractions in place.</p><h2 id="other-stuff" tabindex="-1">Other stuff <a class="header-anchor" href="#other-stuff" aria-label="Permalink to “Other stuff”"></a></h2><p>Get start with the packages:</p><ul><li><a href="/packages/observer/">@logosdx/observer</a></li><li><a href="/packages/utils/">@logosdx/utils</a></li><li><a href="/packages/fetch/">@logosdx/fetch</a></li><li><a href="/packages/hooks.html">@logosdx/hooks</a></li><li><a href="/packages/dom/">@logosdx/dom</a></li><li><a href="/packages/storage/">@logosdx/storage</a></li><li><a href="/packages/localize/">@logosdx/localize</a></li><li><a href="/packages/slides.html">@logosdx/slides</a></li><li><a href="/packages/react.html">@logosdx/react</a></li></ul></div></div></main><footer class="VPDocFooter" data-v-d420190f data-v-d452acd4><!--[--><!--]--><div class="edit-info" data-v-d452acd4><div class="edit-link" data-v-d452acd4><a class="VPLink link vp-external-link-icon no-icon edit-link-button" href="https://github.com/logosdx/monorepo/edit/master/docs/getting-started.md" target="_blank" rel="noreferrer" data-v-d452acd4><!--[--><span class="vpi-square-pen edit-link-icon" data-v-d452acd4></span> Edit this page on GitHub<!--]--></a></div><!----></div><nav class="prev-next" aria-labelledby="doc-footer-aria-label" data-v-d452acd4><span class="visually-hidden" id="doc-footer-aria-label" data-v-d452acd4>Pager</span><div class="pager" data-v-d452acd4><!----></div><div class="pager" data-v-d452acd4><a class="VPLink link pager-link next" href="/what-is-logosdx.html" data-v-d452acd4><!--[--><span class="desc" data-v-d452acd4>Next page</span><span class="title" data-v-d452acd4>What is LogosDX?</span><!--]--></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><footer class="VPFooter has-sidebar" data-v-f534aa51 data-v-456dda6d><div class="container" data-v-456dda6d><p class="message" data-v-456dda6d><a href="https://github.com/logosdx/monorepo/blob/master/LICENSE">BSD-3-Clause License</a> · <a href="/llms.txt">llms.txt</a> · <a href="/llms-full.txt">llms-full.txt</a></p><p class="copyright" data-v-456dda6d>Copyright © 2023+ Logos DX</p></div></footer><!--[--><!--]--></div></div>
<script>window.__VP_HASH_MAP__=JSON.parse("{\"design_fetch-policy-config-coherence.md\":\"CcEuaGVP\",\"design_fetch-resolve-on-response.md\":\"ViBgeuM-\",\"design_slides.md\":\"BRte8s47\",\"documentation-guideline.md\":\"CnAKziju\",\"getting-started.md\":\"B-U1EswA\",\"index.md\":\"DYOvNR3p\",\"packages_dom_animate.md\":\"BNIB1mxx\",\"packages_dom_aria.md\":\"5_A_qshx\",\"packages_dom_events.md\":\"D4GMByMf\",\"packages_dom_index.md\":\"YWgVz8g5\",\"packages_dom_observers.md\":\"DP9n8XaM\",\"packages_dom_selection.md\":\"eej_HdNZ\",\"packages_dom_styling.md\":\"BWGwEThn\",\"packages_dom_templates.md\":\"D-dTJPI5\",\"packages_fetch_advanced.md\":\"CnAoTHaz\",\"packages_fetch_configuration.md\":\"NECA6vMo\",\"packages_fetch_events.md\":\"DYKMoQR_\",\"packages_fetch_hooks.md\":\"BOyAddiU\",\"packages_fetch_index.md\":\"BDpAdfnk\",\"packages_fetch_plugins.md\":\"BSTbuZTN\",\"packages_fetch_policies.md\":\"DX7EbFFj\",\"packages_fetch_requests.md\":\"CCx6aMmI\",\"packages_fetch_resilience.md\":\"Dhw2soZR\",\"packages_hooks.md\":\"DOip-QiZ\",\"packages_localize_api.md\":\"ZUlbe650\",\"packages_localize_async-loading.md\":\"CUZ0LNgn\",\"packages_localize_events.md\":\"B9TS6bft\",\"packages_localize_index.md\":\"C2UZbrdt\",\"packages_localize_intl.md\":\"zmeZdBPf\",\"packages_localize_namespaces.md\":\"CYSuN4AY\",\"packages_localize_pluralization.md\":\"DOv75Jom\",\"packages_localize_translations.md\":\"BXreTWBC\",\"packages_localize_type-extractor.md\":\"D1H0sCnm\",\"packages_observer_advanced.md\":\"CyY9YbDP\",\"packages_observer_events.md\":\"DXoHxvpZ\",\"packages_observer_generators.md\":\"C4Y3hJR2\",\"packages_observer_index.md\":\"BpAFBCEn\",\"packages_observer_queues.md\":\"COqXYd8V\",\"packages_observer_relay.md\":\"Ba_j453p\",\"packages_react.md\":\"BVcPcpJy\",\"packages_slides.md\":\"C4lY4Ik7\",\"packages_state-machine_api.md\":\"CmQOIdVx\",\"packages_state-machine_guide.md\":\"BNGYN7OX\",\"packages_state-machine_index.md\":\"CUuG39FF\",\"packages_storage_api.md\":\"D98brlf6\",\"packages_storage_drivers.md\":\"CWwGTLn7\",\"packages_storage_events.md\":\"848SofAD\",\"packages_storage_index.md\":\"CtKo2aKc\",\"packages_utils_data.md\":\"BkQShdCP\",\"packages_utils_error-handling.md\":\"BmuLHpiD\",\"packages_utils_flow-control.md\":\"DIU42jk8\",\"packages_utils_index.md\":\"8_e8pnjR\",\"packages_utils_performance.md\":\"LP2XFP_2\",\"packages_utils_validation.md\":\"_aAB6Hcj\",\"plans_2026-03-03-api-hooks-design.md\":\"DJWP9RCD\",\"plans_2026-03-03-api-hooks-plan.md\":\"DDvs-Uli\",\"public_llm_dom.md\":\"ZlOorotg\",\"public_llm_fetch.md\":\"DFdA03Y0\",\"public_llm_hooks.md\":\"DL9wut8n\",\"public_llm_localize.md\":\"BKhC4z5f\",\"public_llm_observer.md\":\"CZ3VJANL\",\"public_llm_react.md\":\"B7IforCo\",\"public_llm_slides.md\":\"Cel8-hAH\",\"public_llm_state-machine.md\":\"CUe2A4AR\",\"public_llm_storage.md\":\"tjmsaHHy\",\"public_llm_utils.md\":\"DiBRltjQ\",\"spec_config-update-fns.md\":\"CNU-ekr-\",\"spec_fetch-policy-config-coherence.md\":\"Ca5u37Ej\",\"spec_fetch-resolve-on-response.md\":\"CW9yvbTc\",\"spec_hooks-add-pipe.md\":\"DpeM1e9S\",\"spec_slides.md\":\"BPaMT8vm\",\"what-is-logosdx.md\":\"CpOjF3tn\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Logos DX\",\"description\":\"Focused TypeScript utilities for building JS apps in any runtime\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"logo\":\"/images/app-icon-red.png\",\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/logosdx/monorepo\"}],\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"TypeDocs\",\"link\":\"https://typedoc.logosdx.dev\"},{\"text\":\"llms.txt\",\"link\":\"/llms.txt\"}],\"sidebar\":[{\"text\":\"Introduction\",\"items\":[{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"What is LogosDX?\",\"link\":\"/what-is-logosdx\"}]},{\"text\":\"Packages\",\"items\":[{\"text\":\"Observer\",\"link\":\"/packages/observer/\",\"collapsed\":true,\"items\":[{\"text\":\"Events\",\"link\":\"/packages/observer/events\"},{\"text\":\"Generators\",\"link\":\"/packages/observer/generators\"},{\"text\":\"Queues\",\"link\":\"/packages/observer/queues\"},{\"text\":\"Relay\",\"link\":\"/packages/observer/relay\"},{\"text\":\"Advanced\",\"link\":\"/packages/observer/advanced\"}]},{\"text\":\"Utils\",\"link\":\"/packages/utils/\",\"collapsed\":true,\"items\":[{\"text\":\"Error Handling\",\"link\":\"/packages/utils/error-handling\"},{\"text\":\"Flow Control\",\"link\":\"/packages/utils/flow-control\"},{\"text\":\"Data Operations\",\"link\":\"/packages/utils/data\"},{\"text\":\"Performance & Caching\",\"link\":\"/packages/utils/performance\"},{\"text\":\"Validation & Type Guards\",\"link\":\"/packages/utils/validation\"}]},{\"text\":\"Fetch\",\"link\":\"/packages/fetch/\",\"collapsed\":true,\"items\":[{\"text\":\"Configuration\",\"link\":\"/packages/fetch/configuration\"},{\"text\":\"Making Requests\",\"link\":\"/packages/fetch/requests\"},{\"text\":\"Hooks\",\"link\":\"/packages/fetch/hooks\"},{\"text\":\"Plugins\",\"link\":\"/packages/fetch/plugins\"},{\"text\":\"Resilience\",\"link\":\"/packages/fetch/resilience\"},{\"text\":\"Policies\",\"link\":\"/packages/fetch/policies\"},{\"text\":\"Events\",\"link\":\"/packages/fetch/events\"},{\"text\":\"Advanced\",\"link\":\"/packages/fetch/advanced\"}]},{\"text\":\"State Machine\",\"link\":\"/packages/state-machine/\",\"collapsed\":true,\"items\":[{\"text\":\"API Reference\",\"link\":\"/packages/state-machine/api\"},{\"text\":\"Practical Guide\",\"link\":\"/packages/state-machine/guide\"}]},{\"text\":\"Dom\",\"link\":\"/packages/dom/\",\"collapsed\":true,\"items\":[{\"text\":\"Selection\",\"link\":\"/packages/dom/selection\"},{\"text\":\"Styling\",\"link\":\"/packages/dom/styling\"},{\"text\":\"Aria\",\"link\":\"/packages/dom/aria\"},{\"text\":\"Events\",\"link\":\"/packages/dom/events\"},{\"text\":\"Templates\",\"link\":\"/packages/dom/templates\"},{\"text\":\"Animate\",\"link\":\"/packages/dom/animate\"},{\"text\":\"Observers\",\"link\":\"/packages/dom/observers\"}]},{\"text\":\"Storage\",\"link\":\"/packages/storage/\",\"collapsed\":true,\"items\":[{\"text\":\"API Reference\",\"link\":\"/packages/storage/api\"},{\"text\":\"Drivers\",\"link\":\"/packages/storage/drivers\"},{\"text\":\"Events\",\"link\":\"/packages/storage/events\"}]},{\"text\":\"Localize\",\"link\":\"/packages/localize/\",\"collapsed\":true,\"items\":[{\"text\":\"Translations\",\"link\":\"/packages/localize/translations\"},{\"text\":\"Pluralization\",\"link\":\"/packages/localize/pluralization\"},{\"text\":\"Intl Formatting\",\"link\":\"/packages/localize/intl\"},{\"text\":\"Async Loading\",\"link\":\"/packages/localize/async-loading\"},{\"text\":\"Namespaces\",\"link\":\"/packages/localize/namespaces\"},{\"text\":\"Events\",\"link\":\"/packages/localize/events\"},{\"text\":\"Type Extractor\",\"link\":\"/packages/localize/type-extractor\"},{\"text\":\"API Reference\",\"link\":\"/packages/localize/api\"}]},{\"text\":\"Hooks\",\"link\":\"/packages/hooks\"},{\"text\":\"React\",\"link\":\"/packages/react\"},{\"text\":\"Slides\",\"link\":\"/packages/slides\"}]}],\"editLink\":{\"pattern\":\"https://github.com/logosdx/monorepo/edit/master/docs/:path\",\"text\":\"Edit this page on GitHub\"},\"search\":{\"provider\":\"local\"},\"footer\":{\"message\":\"<a href=\\\"https://github.com/logosdx/monorepo/blob/master/LICENSE\\\">BSD-3-Clause License</a> · <a href=\\\"/llms.txt\\\">llms.txt</a> · <a href=\\\"/llms-full.txt\\\">llms-full.txt</a>\",\"copyright\":\"Copyright © 2023+ Logos DX\"}},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false,\"additionalConfig\":{}}");</script>
</body>
</html>