@relax.js/core 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/dist/DependencyInjection.d.ts +3 -3
  2. package/dist/collections/LinkedList.d.ts +9 -8
  3. package/dist/collections/index.js +1 -1
  4. package/dist/collections/index.js.map +3 -3
  5. package/dist/collections/index.mjs +1 -1
  6. package/dist/collections/index.mjs.map +3 -3
  7. package/dist/di/index.js +1 -1
  8. package/dist/di/index.js.map +2 -2
  9. package/dist/di/index.mjs +1 -1
  10. package/dist/di/index.mjs.map +2 -2
  11. package/dist/elements/index.js +1 -1
  12. package/dist/elements/index.js.map +1 -1
  13. package/dist/forms/FormValidator.d.ts +2 -2
  14. package/dist/forms/ValidationRules.d.ts +2 -6
  15. package/dist/forms/index.js +1 -1
  16. package/dist/forms/index.js.map +3 -3
  17. package/dist/forms/index.mjs +1 -1
  18. package/dist/forms/index.mjs.map +3 -3
  19. package/dist/forms/setFormData.d.ts +39 -1
  20. package/dist/html/index.js +1 -1
  21. package/dist/html/index.js.map +3 -3
  22. package/dist/html/index.mjs +1 -1
  23. package/dist/html/index.mjs.map +3 -3
  24. package/dist/http/ServerSentEvents.d.ts +1 -1
  25. package/dist/http/SimpleWebSocket.d.ts +1 -1
  26. package/dist/http/index.js +1 -1
  27. package/dist/http/index.js.map +3 -3
  28. package/dist/http/index.mjs +1 -1
  29. package/dist/http/index.mjs.map +3 -3
  30. package/dist/i18n/icu.d.ts +1 -1
  31. package/dist/i18n/index.js +1 -1
  32. package/dist/i18n/index.js.map +2 -2
  33. package/dist/i18n/index.mjs +1 -1
  34. package/dist/i18n/index.mjs.map +2 -2
  35. package/dist/index.js +3 -3
  36. package/dist/index.js.map +3 -3
  37. package/dist/index.mjs +3 -3
  38. package/dist/index.mjs.map +3 -3
  39. package/dist/routing/NavigateRouteEvent.d.ts +4 -4
  40. package/dist/routing/index.js +2 -2
  41. package/dist/routing/index.js.map +3 -3
  42. package/dist/routing/index.mjs +3 -3
  43. package/dist/routing/index.mjs.map +3 -3
  44. package/dist/routing/navigation.d.ts +1 -1
  45. package/dist/templates/NodeTemplate.d.ts +1 -1
  46. package/dist/utils/index.d.ts +1 -1
  47. package/dist/utils/index.js +1 -1
  48. package/dist/utils/index.js.map +3 -3
  49. package/dist/utils/index.mjs +1 -1
  50. package/dist/utils/index.mjs.map +3 -3
  51. package/docs/GettingStarted.md +7 -0
  52. package/docs/api.json +34 -12
  53. package/docs/forms/reading-writing.md +101 -1
  54. package/docs/setup/bootstrapping.md +154 -0
  55. package/docs/setup/build-and-deploy.md +183 -0
  56. package/docs/setup/project-structure.md +170 -0
  57. package/docs/setup/vite.md +175 -0
  58. package/package.json +3 -2
  59. package/docs/api/.nojekyll +0 -1
  60. package/docs/api/assets/hierarchy.js +0 -1
  61. package/docs/api/assets/highlight.css +0 -120
  62. package/docs/api/assets/icons.js +0 -18
  63. package/docs/api/assets/icons.svg +0 -1
  64. package/docs/api/assets/main.js +0 -60
  65. package/docs/api/assets/navigation.js +0 -1
  66. package/docs/api/assets/search.js +0 -1
  67. package/docs/api/assets/style.css +0 -1633
  68. package/docs/api/classes/http.WebSocketClient.html +0 -26
  69. package/docs/api/classes/i18n.LocaleChangeEvent.html +0 -66
  70. package/docs/api/classes/index.Blueprint.html +0 -3
  71. package/docs/api/classes/index.BoundNode.html +0 -3
  72. package/docs/api/classes/index.DigitsValidation.html +0 -10
  73. package/docs/api/classes/index.FormValidator.html +0 -32
  74. package/docs/api/classes/index.HttpError.html +0 -13
  75. package/docs/api/classes/index.LinkedList.html +0 -26
  76. package/docs/api/classes/index.NavigateRouteEvent.html +0 -76
  77. package/docs/api/classes/index.Node.html +0 -15
  78. package/docs/api/classes/index.PageSelectedEvent.html +0 -61
  79. package/docs/api/classes/index.Pager.html +0 -4
  80. package/docs/api/classes/index.RangeValidation.html +0 -15
  81. package/docs/api/classes/index.RelaxError.html +0 -17
  82. package/docs/api/classes/index.RequiredValidation.html +0 -10
  83. package/docs/api/classes/index.RouteError.html +0 -11
  84. package/docs/api/classes/index.RouteGuardError.html +0 -12
  85. package/docs/api/classes/index.RouteLink.html +0 -779
  86. package/docs/api/classes/index.RouteTarget.html +0 -788
  87. package/docs/api/classes/index.SSEClient.html +0 -13
  88. package/docs/api/classes/index.SSEDataEvent.html +0 -63
  89. package/docs/api/classes/index.ServiceCollection.html +0 -28
  90. package/docs/api/classes/index.ServiceContainer.html +0 -24
  91. package/docs/api/classes/index.SortChangeEvent.html +0 -61
  92. package/docs/api/classes/index.TableRenderer.html +0 -5
  93. package/docs/api/classes/index.TableSorter.html +0 -4
  94. package/docs/api/enums/index.GuardResult.html +0 -9
  95. package/docs/api/functions/elements.formError.html +0 -6
  96. package/docs/api/functions/elements.selectOne.html +0 -6
  97. package/docs/api/functions/i18n.getCurrentLocale.html +0 -3
  98. package/docs/api/functions/i18n.loadNamespace.html +0 -7
  99. package/docs/api/functions/i18n.loadNamespaces.html +0 -6
  100. package/docs/api/functions/i18n.onMissingTranslation.html +0 -7
  101. package/docs/api/functions/i18n.setLocale.html +0 -7
  102. package/docs/api/functions/i18n.setMessageFormatter.html +0 -7
  103. package/docs/api/functions/i18n.t.html +0 -9
  104. package/docs/api/functions/index.BooleanConverter.html +0 -6
  105. package/docs/api/functions/index.ContainerService.html +0 -13
  106. package/docs/api/functions/index.DateConverter.html +0 -11
  107. package/docs/api/functions/index.Inject.html +0 -16
  108. package/docs/api/functions/index.NumberConverter.html +0 -5
  109. package/docs/api/functions/index.RegisterValidator.html +0 -7
  110. package/docs/api/functions/index.applyPipes.html +0 -17
  111. package/docs/api/functions/index.asyncHandler.html +0 -11
  112. package/docs/api/functions/index.capitalizePipe.html +0 -4
  113. package/docs/api/functions/index.clearPendingNavigations.html +0 -1
  114. package/docs/api/functions/index.compileTemplate.html +0 -26
  115. package/docs/api/functions/index.configure.html +0 -5
  116. package/docs/api/functions/index.createBluePrint.html +0 -1
  117. package/docs/api/functions/index.createConverterFromDataType.html +0 -4
  118. package/docs/api/functions/index.createConverterFromInputType.html +0 -5
  119. package/docs/api/functions/index.createPipeRegistry.html +0 -12
  120. package/docs/api/functions/index.currencyPipe.html +0 -9
  121. package/docs/api/functions/index.datePipe.html +0 -9
  122. package/docs/api/functions/index.daysAgoPipe.html +0 -8
  123. package/docs/api/functions/index.defaultPipe.html +0 -5
  124. package/docs/api/functions/index.defineRoutes.html +0 -8
  125. package/docs/api/functions/index.del.html +0 -8
  126. package/docs/api/functions/index.findRouteByName.html +0 -5
  127. package/docs/api/functions/index.findRouteByUrl.html +0 -4
  128. package/docs/api/functions/index.firstPipe.html +0 -4
  129. package/docs/api/functions/index.generateSequentialId.html +0 -21
  130. package/docs/api/functions/index.get.html +0 -9
  131. package/docs/api/functions/index.getDataConverter.html +0 -11
  132. package/docs/api/functions/index.getParentComponent.html +0 -18
  133. package/docs/api/functions/index.getValidator.html +0 -4
  134. package/docs/api/functions/index.html.html +0 -19
  135. package/docs/api/functions/index.joinPipe.html +0 -5
  136. package/docs/api/functions/index.keysPipe.html +0 -4
  137. package/docs/api/functions/index.lastPipe.html +0 -4
  138. package/docs/api/functions/index.lowercasePipe.html +0 -4
  139. package/docs/api/functions/index.mapFormToClass.html +0 -17
  140. package/docs/api/functions/index.matchRoute.html +0 -5
  141. package/docs/api/functions/index.navigate.html +0 -8
  142. package/docs/api/functions/index.onError.html +0 -8
  143. package/docs/api/functions/index.piecesPipe.html +0 -8
  144. package/docs/api/functions/index.post.html +0 -9
  145. package/docs/api/functions/index.printRoutes.html +0 -2
  146. package/docs/api/functions/index.put.html +0 -9
  147. package/docs/api/functions/index.readData.html +0 -17
  148. package/docs/api/functions/index.registerRouteTarget.html +0 -9
  149. package/docs/api/functions/index.reportError.html +0 -10
  150. package/docs/api/functions/index.request.html +0 -8
  151. package/docs/api/functions/index.resolveValue.html +0 -18
  152. package/docs/api/functions/index.setFetch.html +0 -6
  153. package/docs/api/functions/index.setFormData.html +0 -17
  154. package/docs/api/functions/index.shortenPipe.html +0 -5
  155. package/docs/api/functions/index.startRouting.html +0 -6
  156. package/docs/api/functions/index.ternaryPipe.html +0 -6
  157. package/docs/api/functions/index.trimPipe.html +0 -4
  158. package/docs/api/functions/index.unregisterRouteTarget.html +0 -3
  159. package/docs/api/functions/index.uppercasePipe.html +0 -4
  160. package/docs/api/hierarchy.html +0 -1
  161. package/docs/api/index.html +0 -323
  162. package/docs/api/interfaces/http.SimpleDataEvent.html +0 -3
  163. package/docs/api/interfaces/http.WebSocketAbstraction.html +0 -9
  164. package/docs/api/interfaces/http.WebSocketCodec.html +0 -4
  165. package/docs/api/interfaces/http.WebSocketOptions.html +0 -20
  166. package/docs/api/interfaces/index.CompiledTemplate.html +0 -10
  167. package/docs/api/interfaces/index.DataLoader.html +0 -19
  168. package/docs/api/interfaces/index.EngineConfig.html +0 -11
  169. package/docs/api/interfaces/index.ErrorContext.html +0 -4
  170. package/docs/api/interfaces/index.FormReaderOptions.html +0 -8
  171. package/docs/api/interfaces/index.HttpOptions.html +0 -16
  172. package/docs/api/interfaces/index.HttpResponse.html +0 -17
  173. package/docs/api/interfaces/index.LoadRoute.html +0 -7
  174. package/docs/api/interfaces/index.NavigateOptions.html +0 -7
  175. package/docs/api/interfaces/index.PipeRegistry.html +0 -12
  176. package/docs/api/interfaces/index.RegistrationOptions.html +0 -22
  177. package/docs/api/interfaces/index.RenderTemplate.html +0 -7
  178. package/docs/api/interfaces/index.RequestOptions.html +0 -11
  179. package/docs/api/interfaces/index.Routable.html +0 -10
  180. package/docs/api/interfaces/index.Route.html +0 -13
  181. package/docs/api/interfaces/index.RouteGuard.html +0 -2
  182. package/docs/api/interfaces/index.RouteValue.html +0 -6
  183. package/docs/api/interfaces/index.SSEOptions.html +0 -24
  184. package/docs/api/interfaces/index.ValidationContext.html +0 -8
  185. package/docs/api/interfaces/index.ValidatorOptions.html +0 -14
  186. package/docs/api/media/Architecture.md +0 -333
  187. package/docs/api/media/DependencyInjection.md +0 -277
  188. package/docs/api/media/GettingStarted.md +0 -231
  189. package/docs/api/media/HttpClient.md +0 -459
  190. package/docs/api/media/Pipes.md +0 -211
  191. package/docs/api/media/Routing.md +0 -332
  192. package/docs/api/media/WhyRelaxjs.md +0 -336
  193. package/docs/api/media/forms.md +0 -99
  194. package/docs/api/media/html.md +0 -175
  195. package/docs/api/media/i18n.md +0 -354
  196. package/docs/api/media/utilities.md +0 -143
  197. package/docs/api/media/validation.md +0 -351
  198. package/docs/api/modules/collections_Index.html +0 -1
  199. package/docs/api/modules/di.html +0 -1
  200. package/docs/api/modules/elements.html +0 -1
  201. package/docs/api/modules/forms.html +0 -1
  202. package/docs/api/modules/html.html +0 -1
  203. package/docs/api/modules/http.html +0 -1
  204. package/docs/api/modules/i18n.html +0 -1
  205. package/docs/api/modules/index.html +0 -1
  206. package/docs/api/modules/routing.html +0 -1
  207. package/docs/api/modules/utils.html +0 -1
  208. package/docs/api/modules.html +0 -1
  209. package/docs/api/types/http.WebSocketFactory.html +0 -2
  210. package/docs/api/types/i18n.MessageFormatter.html +0 -3
  211. package/docs/api/types/i18n.MissingTranslationHandler.html +0 -1
  212. package/docs/api/types/index.Constructor.html +0 -7
  213. package/docs/api/types/index.ConverterFunc.html +0 -2
  214. package/docs/api/types/index.DataType.html +0 -2
  215. package/docs/api/types/index.InputType.html +0 -2
  216. package/docs/api/types/index.PipeFunction.html +0 -6
  217. package/docs/api/types/index.RouteData.html +0 -1
  218. package/docs/api/types/index.RouteMatchResult.html +0 -9
  219. package/docs/api/types/index.RouteParamType.html +0 -1
  220. package/docs/api/types/index.RouteSegmentType.html +0 -2
  221. package/docs/api/types/index.SSEEventFactory.html +0 -5
  222. package/docs/api/types/index.ServiceScope.html +0 -10
  223. package/docs/api/types/index.SortColumn.html +0 -3
  224. package/docs/api/variables/i18n.formatICU.html +0 -3
  225. package/docs/api/variables/index.container.html +0 -6
  226. package/docs/api/variables/index.defaultPipes.html +0 -6
  227. package/docs/api/variables/index.internalRoutes.html +0 -1
  228. package/docs/api/variables/index.serviceCollection.html +0 -6
@@ -1,231 +0,0 @@
1
- Getting Started
2
- ===============
3
-
4
- Relaxjs is designed for gradual adoption. Start with full control using vanilla web components, then progressively add features like templating and routing as your app grows.
5
-
6
- ## Installation
7
-
8
- ```
9
- npm i -S @relax.js/core
10
- ```
11
-
12
- ---
13
-
14
- ## Level 1: Plain Web Components
15
-
16
- Start with standard custom elements. No Relaxjs features are needed, just your components in HTML.
17
-
18
- ```html
19
- <!-- index.html -->
20
- <body>
21
- <my-header></my-header>
22
- <my-content></my-content>
23
- </body>
24
- ```
25
-
26
- ```ts
27
- // my-header.ts
28
- export class MyHeader extends HTMLElement {
29
- connectedCallback() {
30
- this.innerHTML = `<h1>My App</h1>`;
31
- }
32
- }
33
- customElements.define('my-header', MyHeader);
34
- ```
35
-
36
- You're in complete control. Relaxjs components like `<r-input>`, `<r-button>`, etc. work alongside your own.
37
-
38
- ---
39
-
40
- ## Level 2: Add Templating with `html()`
41
-
42
- When your components get complex, use `html()` for reactive templates and data binding.
43
-
44
- ```ts
45
- import { html } from '@relax.js/core/html';
46
-
47
- export class UserCard extends HTMLElement {
48
- private user = { name: 'Alice', role: 'Admin' };
49
-
50
- connectedCallback() {
51
- const result = html`
52
- <div class="card">
53
- <h2>{{name}}</h2>
54
- <span>{{role}}</span>
55
- <button onclick=${() => this.edit()}>Edit</button>
56
- </div>
57
- `(this.user);
58
- this.appendChild(result.fragment);
59
- }
60
-
61
- private edit() {
62
- // Handle edit
63
- }
64
- }
65
- customElements.define('user-card', UserCard);
66
- ```
67
-
68
- Still no routing. You control navigation yourself.
69
-
70
- ---
71
-
72
- ## Level 3: Manual Navigation
73
-
74
- Swap content programmatically while staying in full control.
75
-
76
- ```ts
77
- import { html } from '@relax.js/core/html';
78
-
79
- export class AppShell extends HTMLElement {
80
- private main!: HTMLElement;
81
-
82
- connectedCallback() {
83
- const result = html`
84
- <nav>
85
- <a onclick=${() => this.showMain()}>Home</a>
86
- <a onclick=${() => this.showSettings()}>Settings</a>
87
- </nav>
88
- <main></main>
89
- `({});
90
- this.appendChild(result.fragment);
91
- this.main = this.querySelector('main')!;
92
- this.main.innerHTML = '<home-page></home-page>';
93
- }
94
-
95
- private showMain() {
96
- this.main.innerHTML = '<home-page></home-page>';
97
- }
98
- private showSettings() {
99
- this.main.innerHTML = '<settings-page></settings-page>';
100
- }
101
- }
102
- customElements.define('app-shell', AppShell);
103
- ```
104
-
105
- ---
106
-
107
- ## Level 4: Simple Routing
108
-
109
- When your app has multiple pages and you want URL-based navigation, add routing.
110
-
111
- ```ts
112
- import { Route, defineRoutes, startRouting } from '@relax.js/core/routing';
113
-
114
- const routes: Route[] = [
115
- { name: 'home', path: '/', componentTagName: 'home-page' },
116
- { name: 'settings', path: '/settings', componentTagName: 'settings-page' },
117
- { name: 'profile', path: '/profile/:id', componentTagName: 'profile-page' },
118
- ];
119
-
120
- defineRoutes(routes);
121
- startRouting();
122
- ```
123
-
124
- ```html
125
- <!-- index.html -->
126
- <body>
127
- <nav>
128
- <r-link name="home">Home</r-link>
129
- <r-link name="settings">Settings</r-link>
130
- <r-link name="profile" param-id="123">Profile</r-link>
131
- </nav>
132
- <r-route-target></r-route-target>
133
- </body>
134
- ```
135
-
136
- The `<r-link>` component handles navigation by route name. Use `param-*` attributes for route parameters. The `<r-route-target>` renders the matched component.
137
-
138
- ---
139
-
140
- ## Level 5: Programmatic Navigation
141
-
142
- Use `navigate()` for code-driven navigation.
143
-
144
- ```ts
145
- import { navigate } from '@relax.js/core/routing';
146
-
147
- // Navigate by route name with params
148
- navigate('profile', { params: { id: '123' } });
149
-
150
- // Navigate by path
151
- navigate('/settings');
152
- ```
153
-
154
- ---
155
-
156
- ## Level 6: Route Guards
157
-
158
- Protect routes with guards when you need authentication or authorization.
159
-
160
- ```ts
161
- // guards.ts
162
- import { RouteGuard, RouteMatchResult, GuardResult, navigate } from '@relax.js/core/routing';
163
-
164
- export class AuthGuard implements RouteGuard {
165
- check(route: RouteMatchResult): GuardResult {
166
- const token = localStorage.getItem('token');
167
- if (!token) {
168
- navigate('login');
169
- return GuardResult.Stop;
170
- }
171
- return GuardResult.Continue;
172
- }
173
- }
174
- ```
175
-
176
- ```ts
177
- // app.ts
178
- import { Route, defineRoutes } from '@relax.js/core/routing';
179
- import { AuthGuard } from './guards';
180
-
181
- const authGuard = new AuthGuard();
182
-
183
- const routes: Route[] = [
184
- { name: 'login', path: '/login', componentTagName: 'login-page' },
185
- { name: 'home', path: '/', componentTagName: 'home-page', guards: [authGuard] },
186
- { name: 'admin', path: '/admin', componentTagName: 'admin-page', guards: [authGuard] },
187
- ];
188
-
189
- defineRoutes(routes);
190
- ```
191
-
192
- ---
193
-
194
- ## Level 7: Layouts
195
-
196
- Use different HTML pages for distinct UI structures. Each layout is a separate static HTML file.
197
-
198
- ```
199
- /index.html <- default layout (authenticated app)
200
- /public.html <- public layout (login, register)
201
- ```
202
-
203
- ```ts
204
- const routes: Route[] = [
205
- // Routes using public.html
206
- { name: 'login', path: '/login', componentTagName: 'login-page', layout: 'public' },
207
- { name: 'register', path: '/register', componentTagName: 'register-page', layout: 'public' },
208
-
209
- // Routes using index.html (default)
210
- { name: 'dashboard', path: '/', componentTagName: 'dashboard-page' },
211
- { name: 'settings', path: '/settings', componentTagName: 'settings-page' },
212
- ];
213
- ```
214
-
215
- When navigating between layouts, the router redirects to the appropriate HTML file automatically.
216
-
217
- ---
218
-
219
- ## Summary
220
-
221
- | Level | Features | Use When |
222
- |-------|----------|----------|
223
- | 1 | Plain components | Starting out, simple pages |
224
- | 2 | `html()` templating | Complex component rendering |
225
- | 3 | Manual navigation | Few views, full control needed |
226
- | 4 | Basic routing | Multiple pages, URL sync needed |
227
- | 5 | `navigate()` | Code-driven navigation |
228
- | 6 | Guards | Auth/access control |
229
- | 7 | Layouts | Shared UI structure |
230
-
231
- Start at Level 1 and add features as your app requires them. You're always in control.
@@ -1,459 +0,0 @@
1
- # HTTP Client
2
-
3
- Type-safe HTTP module built on fetch() with automatic JWT handling.
4
-
5
- ## Quick Start
6
-
7
- ```typescript
8
- import { configure, get, post } from '@relax.js/core/http';
9
-
10
- configure({ baseUrl: '/api/v1' });
11
-
12
- // GET request
13
- const response = await get('/users');
14
- const users = response.as<User[]>();
15
-
16
- // POST request
17
- const result = await post('/users', JSON.stringify({ name: 'John' }));
18
- ```
19
-
20
- ## Configuration
21
-
22
- Call `configure()` once at app startup to set defaults for all requests:
23
-
24
- ```typescript
25
- import { configure } from '@relax.js/core/http';
26
-
27
- configure({
28
- baseUrl: '/api/v1',
29
- contentType: 'application/json',
30
- bearerTokenName: 'authToken'
31
- });
32
- ```
33
-
34
- ```typescript
35
- interface HttpOptions {
36
- baseUrl?: string; // Base URL prepended to all requests
37
- contentType?: string; // Default content type (default: 'application/json')
38
- bearerTokenName?: string; // JWT token key in localStorage (default: 'jwt', null to disable)
39
- timeout?: number; // Default request timeout in milliseconds
40
- }
41
- ```
42
-
43
- ### Request Timeouts
44
-
45
- Set a default timeout for all requests:
46
-
47
- ```typescript
48
- configure({
49
- baseUrl: '/api',
50
- timeout: 10000 // 10 seconds
51
- });
52
- ```
53
-
54
- Requests that exceed the timeout are automatically aborted. Per-request signals override the default timeout:
55
-
56
- ```typescript
57
- // Custom timeout for a slow endpoint
58
- await get('/reports/generate', null, {
59
- signal: AbortSignal.timeout(60000)
60
- });
61
-
62
- // Manual abort control
63
- const controller = new AbortController();
64
- await get('/users', null, { signal: controller.signal });
65
- controller.abort();
66
- ```
67
-
68
- ## HTTP Methods
69
-
70
- All methods are standalone functions. They return `Promise<HttpResponse>`.
71
-
72
- ### GET
73
-
74
- ```typescript
75
- import { get } from '@relax.js/core/http';
76
-
77
- // Simple GET
78
- const response = await get('/users');
79
-
80
- // With query parameters
81
- const filtered = await get('/users', {
82
- status: 'active',
83
- role: 'admin'
84
- });
85
- // Results in: /api/v1/users?status=active&role=admin
86
- ```
87
-
88
- ### POST
89
-
90
- ```typescript
91
- import { post } from '@relax.js/core/http';
92
-
93
- const user = { name: 'John', email: 'john@example.com' };
94
- const response = await post('/users', JSON.stringify(user));
95
-
96
- if (response.success) {
97
- const created = response.as<User>();
98
- console.log('Created user:', created.id);
99
- }
100
- ```
101
-
102
- ### PUT
103
-
104
- ```typescript
105
- import { put } from '@relax.js/core/http';
106
-
107
- const updates = { name: 'John Updated' };
108
- const response = await put('/users/123', JSON.stringify(updates));
109
- ```
110
-
111
- ### DELETE
112
-
113
- The function is named `del` (not `delete`, which is a reserved word):
114
-
115
- ```typescript
116
- import { del } from '@relax.js/core/http';
117
-
118
- const response = await del('/users/123');
119
- if (response.success) {
120
- console.log('User deleted');
121
- }
122
- ```
123
-
124
- ### Generic Request
125
-
126
- Use `request()` for full control over the request:
127
-
128
- ```typescript
129
- import { request } from '@relax.js/core/http';
130
-
131
- const response = await request('/users', {
132
- method: 'POST',
133
- headers: {
134
- 'Content-Type': 'application/json',
135
- 'X-Custom-Header': 'value'
136
- },
137
- body: JSON.stringify(data),
138
- credentials: 'include'
139
- });
140
- ```
141
-
142
- ## Response Handling
143
-
144
- All methods return an `HttpResponse`:
145
-
146
- ```typescript
147
- interface HttpResponse {
148
- success: boolean; // true for 2xx responses
149
- statusCode: number; // HTTP status code
150
- statusReason: string; // HTTP status text
151
- contentType: string | null; // Response content type
152
- body: unknown; // Parsed JSON body (success) or raw text (error)
153
- charset: string | null; // Response charset
154
- as<T>(): T; // Type-cast body (throws on error responses)
155
- }
156
- ```
157
-
158
- ### Type-Safe Responses
159
-
160
- ```typescript
161
- interface User {
162
- id: number;
163
- name: string;
164
- email: string;
165
- }
166
-
167
- const response = await get('/users/123');
168
-
169
- if (response.success) {
170
- const user = response.as<User>();
171
- displayUser(user);
172
- } else {
173
- console.error(`Error ${response.statusCode}: ${response.body}`);
174
- }
175
- ```
176
-
177
- ### 204 No Content
178
-
179
- Responses with status 204 return `null` as the body (no JSON parsing attempted).
180
-
181
- ## Authentication
182
-
183
- JWT tokens are automatically read from localStorage and added as `Authorization: Bearer <token>`:
184
-
185
- ```typescript
186
- // Login and store token
187
- const loginResponse = await post('/auth/login', JSON.stringify({
188
- username: 'user',
189
- password: 'pass'
190
- }));
191
-
192
- if (loginResponse.success) {
193
- const { token } = loginResponse.as<{ token: string }>();
194
- localStorage.setItem('jwt', token);
195
- }
196
-
197
- // All subsequent requests include the Authorization header automatically
198
- const protectedData = await get('/protected/resource');
199
- ```
200
-
201
- ### Disabling Auto-Auth
202
-
203
- ```typescript
204
- configure({
205
- baseUrl: '/api/public',
206
- bearerTokenName: null // Disable JWT handling
207
- });
208
- ```
209
-
210
- ### Custom Token Name
211
-
212
- ```typescript
213
- configure({
214
- bearerTokenName: 'auth_token' // Reads from localStorage.getItem('auth_token')
215
- });
216
- ```
217
-
218
- ## Error Handling
219
-
220
- ```typescript
221
- import { get, HttpError } from '@relax.js/core/http';
222
-
223
- try {
224
- const response = await get('/users/999');
225
-
226
- if (!response.success) {
227
- throw new HttpError(response);
228
- }
229
-
230
- return response.as<User>();
231
- } catch (error) {
232
- if (error instanceof HttpError) {
233
- console.error(`HTTP ${error.response.statusCode}: ${error.message}`);
234
- } else {
235
- console.error('Network error:', error);
236
- }
237
- }
238
- ```
239
-
240
- ## Testing
241
-
242
- Replace the global fetch implementation for unit tests:
243
-
244
- ```typescript
245
- import { setFetch, get, configure } from '@relax.js/core/http';
246
-
247
- // Mock fetch for tests
248
- setFetch(async (url, options) => {
249
- return new Response(JSON.stringify({ id: 1, name: 'Test User' }), {
250
- status: 200,
251
- headers: { 'content-type': 'application/json' }
252
- });
253
- });
254
-
255
- configure({ baseUrl: '/api' });
256
- const response = await get('/users/1');
257
- const user = response.as<User>();
258
- // user === { id: 1, name: 'Test User' }
259
-
260
- // Restore real fetch
261
- setFetch();
262
- ```
263
-
264
- ## WebSocket Client
265
-
266
- Type-safe WebSocket client with automatic reconnection and message queuing.
267
-
268
- ```typescript
269
- import { WebSocketClient } from '@relax.js/core/http';
270
-
271
- interface ChatMessage {
272
- user: string;
273
- text: string;
274
- }
275
-
276
- const ws = new WebSocketClient<ChatMessage>('wss://chat.example.com', {
277
- autoReconnect: true,
278
- reconnectDelay: 1000,
279
- maxReconnectDelay: 30000,
280
- onConnect: (socket) => console.log('Connected'),
281
- onClose: (socket) => console.log('Disconnected')
282
- });
283
-
284
- ws.connect();
285
-
286
- // Send messages (queued automatically if disconnected)
287
- ws.send({ user: 'John', text: 'Hello!' });
288
-
289
- // Receive messages
290
- while (ws.connected) {
291
- try {
292
- const message = await ws.receive();
293
- console.log(`${message.user}: ${message.text}`);
294
- } catch (error) {
295
- console.log('Connection closed');
296
- break;
297
- }
298
- }
299
-
300
- ws.disconnect();
301
- ```
302
-
303
- ### Features
304
-
305
- - **Auto-reconnect**: Automatically reconnects with exponential backoff (enabled by default)
306
- - **Message queuing**: Messages sent while disconnected are queued and sent on reconnect
307
- - **Type-safe**: Generic type parameter for message types
308
- - **JSON by default**: Automatically serializes/deserializes JSON messages
309
- - **Connection state**: Check `connected` property for current state
310
- - **Graceful disconnect**: Call `disconnect()` to close without auto-reconnect
311
-
312
- ### WebSocket Options
313
-
314
- ```typescript
315
- interface WebSocketOptions<TMessage> {
316
- codec?: WebSocketCodec<TMessage>; // Custom message encoding
317
- autoReconnect?: boolean; // Auto-reconnect (default: true)
318
- reconnectDelay?: number; // Initial reconnect delay in ms (default: 1000)
319
- maxReconnectDelay?: number; // Max reconnect delay in ms (default: 30000)
320
- onConnect?: (socket: WebSocketClient<TMessage>) => void;
321
- onClose?: (socket: WebSocketClient<TMessage>) => void;
322
- }
323
- ```
324
-
325
- ### Exponential Backoff
326
-
327
- When auto-reconnect is enabled, the client uses exponential backoff:
328
-
329
- | Attempt | Delay (with defaults) |
330
- |---------|----------------------|
331
- | 1 | 1s |
332
- | 2 | 2s |
333
- | 3 | 4s |
334
- | 4 | 8s |
335
- | 5 | 16s |
336
- | 6+ | 30s (max) |
337
-
338
- The delay resets to `reconnectDelay` after a successful connection.
339
-
340
- ### Custom Message Codec
341
-
342
- ```typescript
343
- interface WebSocketCodec<TMessage> {
344
- encode(data: TMessage): string | ArrayBufferLike | Blob | ArrayBufferView;
345
- decode(data: string | ArrayBufferLike | Blob | ArrayBufferView): TMessage;
346
- }
347
-
348
- const ws = new WebSocketClient<Message>('wss://api.example.com', {
349
- codec: {
350
- encode(msg: Message): string {
351
- return msgpack.encode(msg);
352
- },
353
- decode(data: string): Message {
354
- return msgpack.decode(data);
355
- }
356
- }
357
- });
358
- ```
359
-
360
- ### Receive Behavior
361
-
362
- `receive()` returns a Promise that resolves when a message arrives. Only one `receive()` call can be active at a time:
363
-
364
- ```typescript
365
- // Correct: sequential receives
366
- const msg1 = await ws.receive();
367
- const msg2 = await ws.receive();
368
-
369
- // Error: concurrent receives throw
370
- const [msg1, msg2] = await Promise.all([ws.receive(), ws.receive()]); // Throws!
371
- ```
372
-
373
- The promise rejects if the connection closes while waiting:
374
-
375
- ```typescript
376
- try {
377
- const message = await ws.receive();
378
- handleMessage(message);
379
- } catch (error) {
380
- // error.message === 'WebSocket connection closed'
381
- console.log('Connection lost');
382
- }
383
- ```
384
-
385
- ### Testing WebSocket
386
-
387
- Pass a factory function instead of a URL:
388
-
389
- ```typescript
390
- import { WebSocketClient, WebSocketAbstraction, WebSocketFactory } from '@relax.js/core/http';
391
-
392
- const mockSocket: WebSocketAbstraction = {
393
- onopen: null,
394
- onerror: null,
395
- onclose: null,
396
- onmessage: null,
397
- send: vi.fn(),
398
- close: vi.fn()
399
- };
400
-
401
- const factory: WebSocketFactory = () => mockSocket;
402
- const ws = new WebSocketClient<Message>(factory);
403
- ws.connect();
404
-
405
- // Simulate server message
406
- mockSocket.onmessage?.({ data: '{"text": "hello"}' });
407
- ```
408
-
409
- ## API Reference
410
-
411
- ### Functions
412
-
413
- | Function | Description |
414
- |----------|-------------|
415
- | `configure(options)` | Set module-wide defaults (base URL, content type, JWT) |
416
- | `get(url, queryString?, options?)` | GET request with optional query parameters |
417
- | `post(url, body, options?)` | POST request with body |
418
- | `put(url, body, options?)` | PUT request with body |
419
- | `del(url, options?)` | DELETE request |
420
- | `request(url, options?)` | Generic request with full RequestInit options |
421
- | `setFetch(fn?)` | Replace fetch implementation for testing |
422
-
423
- ### WebSocketClient
424
-
425
- | Method/Property | Description |
426
- |-----------------|-------------|
427
- | `connect()` | Establish WebSocket connection |
428
- | `disconnect()` | Close connection without auto-reconnect |
429
- | `send(data)` | Send message (queued if disconnected) |
430
- | `receive()` | Receive next message (rejects on close) |
431
- | `connected` | `boolean` - Current connection state |
432
-
433
- ### Exports
434
-
435
- ```typescript
436
- // HTTP
437
- import {
438
- configure,
439
- get,
440
- post,
441
- put,
442
- del,
443
- request,
444
- setFetch,
445
- HttpOptions,
446
- HttpResponse,
447
- HttpError,
448
- RequestOptions
449
- } from '@relax.js/core/http';
450
-
451
- // WebSocket
452
- import {
453
- WebSocketClient,
454
- WebSocketOptions,
455
- WebSocketCodec,
456
- WebSocketAbstraction,
457
- WebSocketFactory
458
- } from '@relax.js/core/http';
459
- ```