@relax.js/core 1.0.3 → 1.0.4

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 (234) hide show
  1. package/README.md +194 -188
  2. package/dist/DependencyInjection.d.ts +42 -24
  3. package/dist/di/index.js +1 -1
  4. package/dist/di/index.js.map +3 -3
  5. package/dist/di/index.mjs +1 -1
  6. package/dist/di/index.mjs.map +3 -3
  7. package/dist/errors.d.ts +20 -0
  8. package/dist/forms/FormValidator.d.ts +1 -20
  9. package/dist/forms/ValidationRules.d.ts +2 -0
  10. package/dist/forms/index.js +1 -1
  11. package/dist/forms/index.js.map +4 -4
  12. package/dist/forms/index.mjs +1 -1
  13. package/dist/forms/index.mjs.map +4 -4
  14. package/dist/html/TableRenderer.d.ts +1 -0
  15. package/dist/html/index.js.map +2 -2
  16. package/dist/html/index.mjs.map +2 -2
  17. package/dist/html/template.d.ts +4 -0
  18. package/dist/http/http.d.ts +1 -0
  19. package/dist/http/index.js.map +2 -2
  20. package/dist/http/index.mjs.map +2 -2
  21. package/dist/index.js +3 -3
  22. package/dist/index.js.map +3 -3
  23. package/dist/index.mjs +3 -3
  24. package/dist/index.mjs.map +3 -3
  25. package/dist/routing/index.js +3 -3
  26. package/dist/routing/index.js.map +3 -3
  27. package/dist/routing/index.mjs +3 -3
  28. package/dist/routing/index.mjs.map +3 -3
  29. package/dist/routing/routeTargetRegistry.d.ts +1 -0
  30. package/dist/routing/types.d.ts +2 -1
  31. package/dist/templates/NodeTemplate.d.ts +2 -0
  32. package/dist/utils/index.js +1 -1
  33. package/dist/utils/index.js.map +2 -2
  34. package/dist/utils/index.mjs +1 -1
  35. package/dist/utils/index.mjs.map +2 -2
  36. package/docs/Architecture.md +333 -333
  37. package/docs/DependencyInjection.md +277 -237
  38. package/docs/Errors.md +87 -87
  39. package/docs/GettingStarted.md +231 -231
  40. package/docs/Pipes.md +5 -5
  41. package/docs/Translations.md +167 -312
  42. package/docs/WhyRelaxjs.md +336 -336
  43. package/docs/api/.nojekyll +1 -0
  44. package/docs/api/assets/hierarchy.js +1 -0
  45. package/docs/api/assets/highlight.css +120 -0
  46. package/docs/api/assets/icons.js +18 -0
  47. package/docs/api/assets/icons.svg +1 -0
  48. package/docs/api/assets/main.js +60 -0
  49. package/docs/api/assets/navigation.js +1 -0
  50. package/docs/api/assets/search.js +1 -0
  51. package/docs/api/assets/style.css +1633 -0
  52. package/docs/api/classes/http.WebSocketClient.html +26 -0
  53. package/docs/api/classes/i18n.LocaleChangeEvent.html +66 -0
  54. package/docs/api/classes/index.Blueprint.html +3 -0
  55. package/docs/api/classes/index.BoundNode.html +3 -0
  56. package/docs/api/classes/index.DigitsValidation.html +10 -0
  57. package/docs/api/classes/index.FormValidator.html +32 -0
  58. package/docs/api/classes/index.HttpError.html +13 -0
  59. package/docs/api/classes/index.LinkedList.html +26 -0
  60. package/docs/api/classes/index.NavigateRouteEvent.html +76 -0
  61. package/docs/api/classes/index.Node.html +15 -0
  62. package/docs/api/classes/index.PageSelectedEvent.html +61 -0
  63. package/docs/api/classes/index.Pager.html +4 -0
  64. package/docs/api/classes/index.RangeValidation.html +15 -0
  65. package/docs/api/classes/index.RelaxError.html +17 -0
  66. package/docs/api/classes/index.RequiredValidation.html +10 -0
  67. package/docs/api/classes/index.RouteError.html +11 -0
  68. package/docs/api/classes/index.RouteGuardError.html +12 -0
  69. package/docs/api/classes/index.RouteLink.html +779 -0
  70. package/docs/api/classes/index.RouteTarget.html +788 -0
  71. package/docs/api/classes/index.SSEClient.html +13 -0
  72. package/docs/api/classes/index.SSEDataEvent.html +63 -0
  73. package/docs/api/classes/index.ServiceCollection.html +28 -0
  74. package/docs/api/classes/index.ServiceContainer.html +24 -0
  75. package/docs/api/classes/index.SortChangeEvent.html +61 -0
  76. package/docs/api/classes/index.TableRenderer.html +5 -0
  77. package/docs/api/classes/index.TableSorter.html +4 -0
  78. package/docs/api/enums/index.GuardResult.html +9 -0
  79. package/docs/api/functions/elements.formError.html +6 -0
  80. package/docs/api/functions/elements.selectOne.html +6 -0
  81. package/docs/api/functions/i18n.getCurrentLocale.html +3 -0
  82. package/docs/api/functions/i18n.loadNamespace.html +7 -0
  83. package/docs/api/functions/i18n.loadNamespaces.html +6 -0
  84. package/docs/api/functions/i18n.onMissingTranslation.html +7 -0
  85. package/docs/api/functions/i18n.setLocale.html +7 -0
  86. package/docs/api/functions/i18n.setMessageFormatter.html +7 -0
  87. package/docs/api/functions/i18n.t.html +9 -0
  88. package/docs/api/functions/index.BooleanConverter.html +6 -0
  89. package/docs/api/functions/index.ContainerService.html +13 -0
  90. package/docs/api/functions/index.DateConverter.html +11 -0
  91. package/docs/api/functions/index.Inject.html +16 -0
  92. package/docs/api/functions/index.NumberConverter.html +5 -0
  93. package/docs/api/functions/index.RegisterValidator.html +7 -0
  94. package/docs/api/functions/index.applyPipes.html +17 -0
  95. package/docs/api/functions/index.asyncHandler.html +11 -0
  96. package/docs/api/functions/index.capitalizePipe.html +4 -0
  97. package/docs/api/functions/index.clearPendingNavigations.html +1 -0
  98. package/docs/api/functions/index.compileTemplate.html +26 -0
  99. package/docs/api/functions/index.configure.html +5 -0
  100. package/docs/api/functions/index.createBluePrint.html +1 -0
  101. package/docs/api/functions/index.createConverterFromDataType.html +4 -0
  102. package/docs/api/functions/index.createConverterFromInputType.html +5 -0
  103. package/docs/api/functions/index.createPipeRegistry.html +12 -0
  104. package/docs/api/functions/index.currencyPipe.html +9 -0
  105. package/docs/api/functions/index.datePipe.html +9 -0
  106. package/docs/api/functions/index.daysAgoPipe.html +8 -0
  107. package/docs/api/functions/index.defaultPipe.html +5 -0
  108. package/docs/api/functions/index.defineRoutes.html +8 -0
  109. package/docs/api/functions/index.del.html +8 -0
  110. package/docs/api/functions/index.findRouteByName.html +5 -0
  111. package/docs/api/functions/index.findRouteByUrl.html +4 -0
  112. package/docs/api/functions/index.firstPipe.html +4 -0
  113. package/docs/api/functions/index.generateSequentialId.html +21 -0
  114. package/docs/api/functions/index.get.html +9 -0
  115. package/docs/api/functions/index.getDataConverter.html +11 -0
  116. package/docs/api/functions/index.getParentComponent.html +18 -0
  117. package/docs/api/functions/index.getValidator.html +4 -0
  118. package/docs/api/functions/index.html.html +19 -0
  119. package/docs/api/functions/index.joinPipe.html +5 -0
  120. package/docs/api/functions/index.keysPipe.html +4 -0
  121. package/docs/api/functions/index.lastPipe.html +4 -0
  122. package/docs/api/functions/index.lowercasePipe.html +4 -0
  123. package/docs/api/functions/index.mapFormToClass.html +17 -0
  124. package/docs/api/functions/index.matchRoute.html +5 -0
  125. package/docs/api/functions/index.navigate.html +8 -0
  126. package/docs/api/functions/index.onError.html +8 -0
  127. package/docs/api/functions/index.piecesPipe.html +8 -0
  128. package/docs/api/functions/index.post.html +9 -0
  129. package/docs/api/functions/index.printRoutes.html +2 -0
  130. package/docs/api/functions/index.put.html +9 -0
  131. package/docs/api/functions/index.readData.html +17 -0
  132. package/docs/api/functions/index.registerRouteTarget.html +9 -0
  133. package/docs/api/functions/index.reportError.html +10 -0
  134. package/docs/api/functions/index.request.html +8 -0
  135. package/docs/api/functions/index.resolveValue.html +18 -0
  136. package/docs/api/functions/index.setFetch.html +6 -0
  137. package/docs/api/functions/index.setFormData.html +17 -0
  138. package/docs/api/functions/index.shortenPipe.html +5 -0
  139. package/docs/api/functions/index.startRouting.html +6 -0
  140. package/docs/api/functions/index.ternaryPipe.html +6 -0
  141. package/docs/api/functions/index.trimPipe.html +4 -0
  142. package/docs/api/functions/index.unregisterRouteTarget.html +3 -0
  143. package/docs/api/functions/index.uppercasePipe.html +4 -0
  144. package/docs/api/hierarchy.html +1 -0
  145. package/docs/api/index.html +323 -0
  146. package/docs/api/interfaces/http.SimpleDataEvent.html +3 -0
  147. package/docs/api/interfaces/http.WebSocketAbstraction.html +9 -0
  148. package/docs/api/interfaces/http.WebSocketCodec.html +4 -0
  149. package/docs/api/interfaces/http.WebSocketOptions.html +20 -0
  150. package/docs/api/interfaces/index.CompiledTemplate.html +10 -0
  151. package/docs/api/interfaces/index.DataLoader.html +19 -0
  152. package/docs/api/interfaces/index.EngineConfig.html +11 -0
  153. package/docs/api/interfaces/index.ErrorContext.html +4 -0
  154. package/docs/api/interfaces/index.FormReaderOptions.html +8 -0
  155. package/docs/api/interfaces/index.HttpOptions.html +16 -0
  156. package/docs/api/interfaces/index.HttpResponse.html +17 -0
  157. package/docs/api/interfaces/index.LoadRoute.html +7 -0
  158. package/docs/api/interfaces/index.NavigateOptions.html +7 -0
  159. package/docs/api/interfaces/index.PipeRegistry.html +12 -0
  160. package/docs/api/interfaces/index.RegistrationOptions.html +22 -0
  161. package/docs/api/interfaces/index.RenderTemplate.html +7 -0
  162. package/docs/api/interfaces/index.RequestOptions.html +11 -0
  163. package/docs/api/interfaces/index.Routable.html +10 -0
  164. package/docs/api/interfaces/index.Route.html +13 -0
  165. package/docs/api/interfaces/index.RouteGuard.html +2 -0
  166. package/docs/api/interfaces/index.RouteValue.html +6 -0
  167. package/docs/api/interfaces/index.SSEOptions.html +24 -0
  168. package/docs/api/interfaces/index.ValidationContext.html +8 -0
  169. package/docs/api/interfaces/index.ValidatorOptions.html +14 -0
  170. package/docs/api/media/Architecture.md +333 -0
  171. package/docs/api/media/DependencyInjection.md +277 -0
  172. package/docs/api/media/GettingStarted.md +231 -0
  173. package/docs/api/media/HttpClient.md +459 -0
  174. package/docs/api/media/Pipes.md +211 -0
  175. package/docs/api/media/Routing.md +332 -0
  176. package/docs/api/media/WhyRelaxjs.md +336 -0
  177. package/docs/api/media/forms.md +99 -0
  178. package/docs/api/media/html.md +175 -0
  179. package/docs/api/media/i18n.md +354 -0
  180. package/docs/api/media/utilities.md +143 -0
  181. package/docs/api/media/validation.md +351 -0
  182. package/docs/api/modules/collections_Index.html +1 -0
  183. package/docs/api/modules/di.html +1 -0
  184. package/docs/api/modules/elements.html +1 -0
  185. package/docs/api/modules/forms.html +1 -0
  186. package/docs/api/modules/html.html +1 -0
  187. package/docs/api/modules/http.html +1 -0
  188. package/docs/api/modules/i18n.html +1 -0
  189. package/docs/api/modules/index.html +1 -0
  190. package/docs/api/modules/routing.html +1 -0
  191. package/docs/api/modules/utils.html +1 -0
  192. package/docs/api/modules.html +1 -0
  193. package/docs/api/types/http.WebSocketFactory.html +2 -0
  194. package/docs/api/types/i18n.MessageFormatter.html +3 -0
  195. package/docs/api/types/i18n.MissingTranslationHandler.html +1 -0
  196. package/docs/api/types/index.Constructor.html +7 -0
  197. package/docs/api/types/index.ConverterFunc.html +2 -0
  198. package/docs/api/types/index.DataType.html +2 -0
  199. package/docs/api/types/index.InputType.html +2 -0
  200. package/docs/api/types/index.PipeFunction.html +6 -0
  201. package/docs/api/types/index.RouteData.html +1 -0
  202. package/docs/api/types/index.RouteMatchResult.html +9 -0
  203. package/docs/api/types/index.RouteParamType.html +1 -0
  204. package/docs/api/types/index.RouteSegmentType.html +2 -0
  205. package/docs/api/types/index.SSEEventFactory.html +5 -0
  206. package/docs/api/types/index.ServiceScope.html +10 -0
  207. package/docs/api/types/index.SortColumn.html +3 -0
  208. package/docs/api/variables/i18n.formatICU.html +3 -0
  209. package/docs/api/variables/index.container.html +6 -0
  210. package/docs/api/variables/index.defaultPipes.html +6 -0
  211. package/docs/api/variables/index.internalRoutes.html +1 -0
  212. package/docs/api/variables/index.serviceCollection.html +6 -0
  213. package/docs/api.json +93171 -0
  214. package/docs/elements/dom.md +102 -102
  215. package/docs/forms/creating-form-components.md +924 -924
  216. package/docs/forms/form-api.md +94 -94
  217. package/docs/forms/forms.md +99 -99
  218. package/docs/forms/patterns.md +311 -311
  219. package/docs/forms/reading-writing.md +365 -365
  220. package/docs/forms/validation.md +351 -351
  221. package/docs/html/TableRenderer.md +291 -291
  222. package/docs/html/html.md +175 -175
  223. package/docs/html/index.md +54 -54
  224. package/docs/html/template.md +422 -422
  225. package/docs/http/HttpClient.md +459 -459
  226. package/docs/http/ServerSentEvents.md +184 -184
  227. package/docs/http/index.md +109 -109
  228. package/docs/i18n/i18n.md +49 -4
  229. package/docs/i18n/intl-standard.md +178 -178
  230. package/docs/routing/RouteLink.md +98 -98
  231. package/docs/routing/Routing.md +332 -332
  232. package/docs/routing/layouts.md +207 -207
  233. package/docs/utilities.md +143 -143
  234. package/package.json +4 -3
@@ -0,0 +1,211 @@
1
+ # Pipes
2
+
3
+ Data transformation functions for use in template expressions. Pipes transform values for display without modifying the underlying data.
4
+
5
+ ## Usage
6
+
7
+ In templates with `compileTemplate`:
8
+
9
+ ```html
10
+ <span>{{name | uppercase}}</span>
11
+ <span>{{price | currency}}</span>
12
+ <span>{{description | shorten:50}}</span>
13
+ <span>{{tags | join:, }}</span>
14
+ ```
15
+
16
+ Pipes can be chained:
17
+
18
+ ```html
19
+ <span>{{text | trim | uppercase | shorten:20}}</span>
20
+ ```
21
+
22
+ ## Localization
23
+
24
+ Several pipes are locale-aware and use the [i18n](i18n/i18n.md) system:
25
+
26
+ - `currency` - Formats numbers according to locale
27
+ - `date` - Formats dates according to locale
28
+ - `daysAgo` - Translates "today", "yesterday", "X days ago"
29
+ - `pieces` - Translates piece counts
30
+
31
+ To use localized pipes:
32
+
33
+ ```typescript
34
+ import { setLocale, loadNamespace } from '@relax.js/core/i18n';
35
+
36
+ // Set locale and load pipe translations
37
+ await setLocale('sv');
38
+ await loadNamespace('r-pipes');
39
+
40
+ // Now pipes will output Swedish
41
+ // {{createdAt | daysAgo}} → "idag", "igår", "3 dagar sedan"
42
+ // {{price | currency:SEK}} → "1 234,56 kr"
43
+ ```
44
+
45
+ ### Translation Keys
46
+
47
+ Translation keys in the `r-pipes` namespace:
48
+
49
+ | Key | Message (EN) | Message (SV) |
50
+ |-----|-------------|-------------|
51
+ | `today` | `today` | `idag` |
52
+ | `yesterday` | `yesterday` | `igår` |
53
+ | `daysAgo` | `{count, plural, one {# day ago} other {# days ago}}` | `{count, plural, one {# dag sedan} other {# dagar sedan}}` |
54
+ | `pieces` | `{count, plural, =0 {none} one {one} other {# pcs}}` | `{count, plural, =0 {inga} one {en} other {# st}}` |
55
+
56
+ ### Translation Files
57
+
58
+ Pipe translations are stored in `src/i18n/locales/{locale}/r-pipes.json`:
59
+
60
+ ```json
61
+ {
62
+ "today": "today",
63
+ "yesterday": "yesterday",
64
+ "daysAgo": "{count, plural, one {# day ago} other {# days ago}}",
65
+ "pieces": "{count, plural, =0 {none} one {one} other {# pcs}}"
66
+ }
67
+ ```
68
+
69
+ To add a new locale, create `src/i18n/locales/{locale}/r-pipes.json` with the translated strings.
70
+
71
+ ## Programmatic Usage
72
+
73
+ ```typescript
74
+ import { applyPipes, defaultPipes, createPipeRegistry } from '@relax.js/core/utils';
75
+
76
+ // Apply pipes to a value
77
+ const result = applyPipes('hello world', ['uppercase', 'shorten:8']);
78
+ // Returns: 'HELLO...'
79
+
80
+ // Use the default registry directly
81
+ const upper = defaultPipes.get('uppercase');
82
+ console.log(upper('hello')); // 'HELLO'
83
+
84
+ // Create a fresh registry
85
+ const registry = createPipeRegistry();
86
+ ```
87
+
88
+ ## Built-in Pipes
89
+
90
+ ### Text
91
+
92
+ | Pipe | Description | Example | Result |
93
+ |------|-------------|---------|--------|
94
+ | `uppercase` | Convert to uppercase | `{{"hello" \| uppercase}}` | `HELLO` |
95
+ | `lowercase` | Convert to lowercase | `{{"HELLO" \| lowercase}}` | `hello` |
96
+ | `capitalize` | Capitalize first letter | `{{"hello" \| capitalize}}` | `Hello` |
97
+ | `trim` | Remove leading/trailing whitespace | `{{" hello " \| trim}}` | `hello` |
98
+ | `shorten:n` | Limit to n characters with ellipsis | `{{"hello world" \| shorten:8}}` | `hello...` |
99
+
100
+ ### Formatting (Locale-Aware)
101
+
102
+ These pipes use the current i18n locale for formatting.
103
+
104
+ | Pipe | Description | Example | en | sv |
105
+ |------|-------------|---------|----|----|
106
+ | `currency` | Format as currency (default USD) | `{{1234.5 \| currency}}` | `$1,234.50` | `1 234,50 US$` |
107
+ | `currency:CODE` | Format with specific currency | `{{1234.5 \| currency:SEK}}` | `SEK 1,234.50` | `1 234,50 kr` |
108
+ | `date` | Format date (ISO) | `{{date \| date}}` | `2024-01-15T...` | `2024-01-15T...` |
109
+ | `date:short` | Short date format | `{{date \| date:short}}` | `1/15/2024` | `2024-01-15` |
110
+ | `date:long` | Long date format | `{{date \| date:long}}` | `Monday, January 15, 2024` | `måndag 15 januari 2024` |
111
+ | `daysAgo` | Relative date | `{{date \| daysAgo}}` | `3 days ago` | `3 dagar sedan` |
112
+ | `pieces` | Piece count | `{{3 \| pieces}}` | `3 pcs` | `3 st` |
113
+
114
+ ### Arrays
115
+
116
+ | Pipe | Description | Example | Result |
117
+ |------|-------------|---------|--------|
118
+ | `join` | Join with comma | `{{tags \| join}}` | `a,b,c` |
119
+ | `join:sep` | Join with custom separator | `{{tags \| join: \| }}` | `a \| b \| c` |
120
+ | `first` | Get first element | `{{items \| first}}` | First item |
121
+ | `last` | Get last element | `{{items \| last}}` | Last item |
122
+
123
+ ### Objects
124
+
125
+ | Pipe | Description | Example | Result |
126
+ |------|-------------|---------|--------|
127
+ | `keys` | Get object keys as array | `{{user \| keys}}` | `["name", "email"]` |
128
+
129
+ ### Conditionals
130
+
131
+ | Pipe | Description | Example | Result |
132
+ |------|-------------|---------|--------|
133
+ | `default:val` | Fallback for falsy values | `{{name \| default:Anonymous}}` | Value or `Anonymous` |
134
+ | `ternary:t:f` | Conditional value | `{{active \| ternary:Yes:No}}` | `Yes` or `No` |
135
+
136
+ ## Pipe Arguments
137
+
138
+ Arguments are passed after a colon:
139
+
140
+ ```html
141
+ <!-- Single argument -->
142
+ <span>{{text | shorten:50}}</span>
143
+ <span>{{price | currency:EUR}}</span>
144
+
145
+ <!-- Multiple arguments -->
146
+ <span>{{status | ternary:Active:Inactive}}</span>
147
+ ```
148
+
149
+ ## Configuration
150
+
151
+ Pass a pipe registry to `compileTemplate`:
152
+
153
+ ```typescript
154
+ import { compileTemplate } from '@relax.js/core/html';
155
+ import { createPipeRegistry } from '@relax.js/core/utils';
156
+
157
+ const pipeRegistry = createPipeRegistry();
158
+ const { content, render } = compileTemplate(
159
+ '<span>{{name | uppercase}}</span>',
160
+ { strict: false, pipeRegistry }
161
+ );
162
+ ```
163
+
164
+ ## API Reference
165
+
166
+ ### Types
167
+
168
+ ```typescript
169
+ type PipeFunction = (value: any, ...args: any[]) => any;
170
+
171
+ interface PipeRegistry {
172
+ lookup(name: string): PipeFunction | null;
173
+ get(name: string): PipeFunction; // Throws if not found
174
+ has(name: string): boolean;
175
+ }
176
+ ```
177
+
178
+ ### Functions
179
+
180
+ | Function | Description |
181
+ |----------|-------------|
182
+ | `createPipeRegistry()` | Create a new registry with all built-in pipes |
183
+ | `applyPipes(value, pipes, registry?)` | Apply an array of pipe strings to a value |
184
+ | `defaultPipes` | Pre-created registry instance |
185
+
186
+ ### Individual Pipe Functions
187
+
188
+ All pipes are also exported as individual functions for direct use:
189
+
190
+ ```typescript
191
+ import {
192
+ uppercasePipe,
193
+ lowercasePipe,
194
+ capitalizePipe,
195
+ trimPipe,
196
+ shortenPipe,
197
+ currencyPipe,
198
+ datePipe,
199
+ daysAgoPipe,
200
+ piecesPipe,
201
+ joinPipe,
202
+ firstPipe,
203
+ lastPipe,
204
+ keysPipe,
205
+ defaultPipe,
206
+ ternaryPipe
207
+ } from '@relax.js/core/utils';
208
+
209
+ const result = uppercasePipe('hello'); // 'HELLO'
210
+ const formatted = currencyPipe(1234.56); // '$1,234.56' (depends on locale)
211
+ ```
@@ -0,0 +1,332 @@
1
+ # Routing
2
+
3
+ ## Overview
4
+
5
+ A client-side routing system for single-page applications. The router:
6
+
7
+ - Matches URLs to route configurations
8
+ - Extracts typed parameters from URL segments
9
+ - Manages browser history (back/forward navigation)
10
+ - Dispatches `NavigateRouteEvent` to render components
11
+ - Supports multiple rendering targets (main content, sidebars, modals)
12
+ - Handles layout switching between different HTML shells
13
+
14
+ The router itself only handles matching and navigation. Rendering is handled by [`<r-route-target>`](RoutingTarget.md) components that listen for navigation events.
15
+
16
+ ## Quick Start
17
+
18
+ ```typescript
19
+ import { Route, defineRoutes, startRouting } from '@relax.js/core/routing';
20
+
21
+ const routes: Route[] = [
22
+ { name: 'home', path: '/', componentTagName: 'home-page' },
23
+ { name: 'users', path: '/users', componentTagName: 'user-list' },
24
+ { name: 'user', path: '/users/:id', componentTagName: 'user-profile' },
25
+ ];
26
+
27
+ defineRoutes(routes);
28
+ startRouting();
29
+ ```
30
+
31
+ ```html
32
+ <body>
33
+ <nav>
34
+ <r-link name="home">Home</r-link>
35
+ <r-link name="users">Users</r-link>
36
+ </nav>
37
+ <main>
38
+ <r-route-target></r-route-target>
39
+ </main>
40
+ </body>
41
+ ```
42
+
43
+ ## Route Definition
44
+
45
+ Each route specifies how a URL maps to a component:
46
+
47
+ ```typescript
48
+ interface Route {
49
+ name?: string; // Identifier for programmatic navigation
50
+ path: string; // URL pattern with parameters
51
+ componentTagName?: string; // Custom element tag name
52
+ component?: WebComponentConstructor; // Or class reference
53
+ target?: string; // Named r-route-target (default: unnamed)
54
+ layout?: string; // HTML file for different shells
55
+ guards?: RouteGuard[]; // Access control
56
+ }
57
+ ```
58
+
59
+ ### URL Pattern Syntax
60
+
61
+ | Syntax | Type | Example | Matches |
62
+ |--------|------|---------|---------|
63
+ | `text` | Static segment | `/users` | Exactly "users" |
64
+ | `:name` | String parameter | `/users/:id` | `/users/john` → `{ id: 'john' }` |
65
+ | `;name` | Number parameter | `/orders/;orderId` | `/orders/123` → `{ orderId: 123 }` |
66
+
67
+ Number parameters (`;`) validate that the segment contains only digits and convert to `number` type. String parameters (`:`) accept any value and remain as `string`.
68
+
69
+ ```typescript
70
+ const routes: Route[] = [
71
+ { name: 'home', path: '/' },
72
+ { name: 'user', path: '/users/:userName' }, // String: userName
73
+ { name: 'order', path: '/orders/;orderId' }, // Number: orderId
74
+ { name: 'category', path: '/shop/:category/items' }, // Mixed static + param
75
+ ];
76
+ ```
77
+
78
+ ## Navigation
79
+
80
+ ### Using RouteLink Component
81
+
82
+ The simplest way to navigate is with [`<r-link>`](RouteLink.md):
83
+
84
+ ```html
85
+ <r-link name="home">Home</r-link>
86
+ <r-link name="user" param-userName="john">View Profile</r-link>
87
+ <r-link name="order" param-orderId="123">Order Details</r-link>
88
+ ```
89
+
90
+ ### Programmatic Navigation
91
+
92
+ Use `navigate()` for navigation from code:
93
+
94
+ ```typescript
95
+ import { navigate } from '@relax.js/core/routing';
96
+
97
+ // By route name with parameters
98
+ navigate('user', { params: { userName: 'john' } });
99
+ navigate('order', { params: { orderId: 123 } });
100
+
101
+ // By URL directly
102
+ navigate('/users/john');
103
+ navigate('/orders/123');
104
+
105
+ // To a specific target
106
+ navigate('preview', { params: { id: '42' }, target: 'modal' });
107
+ ```
108
+
109
+ ## Multiple Targets
110
+
111
+ Routes can render in different [`<r-route-target>`](RoutingTarget.md) elements:
112
+
113
+ ```html
114
+ <div class="layout">
115
+ <aside>
116
+ <r-route-target name="sidebar"></r-route-target>
117
+ </aside>
118
+ <main>
119
+ <r-route-target></r-route-target>
120
+ </main>
121
+ </div>
122
+ ```
123
+
124
+ ```typescript
125
+ const routes: Route[] = [
126
+ { name: 'home', path: '/', componentTagName: 'home-page' },
127
+ { name: 'menu', path: '/menu', target: 'sidebar', componentTagName: 'nav-menu' },
128
+ ];
129
+ ```
130
+
131
+ Routes without a `target` property render in the default (unnamed) target.
132
+
133
+ ## Browser History
134
+
135
+ The router integrates with the browser's History API:
136
+
137
+ - `navigate()` calls `history.pushState()` to add entries
138
+ - Back/forward buttons trigger navigation to previous routes (components receive `loadRoute` again)
139
+ - `startRouting()` reads the current URL and navigates on page load
140
+
141
+ URLs display the route path (e.g., `/users/john`), not the HTML file.
142
+
143
+ ## Layouts
144
+
145
+ Different parts of your application may need different HTML shells (navigation, sidebar presence, etc.). See [Layouts](layouts.md) for details.
146
+
147
+ ```typescript
148
+ const routes: Route[] = [
149
+ { name: 'login', path: '/login', componentTagName: 'login-page', layout: 'public' },
150
+ { name: 'dashboard', path: '/dashboard', componentTagName: 'dashboard-page' }, // default layout
151
+ ];
152
+ ```
153
+
154
+ When navigating between layouts, the router redirects to the appropriate HTML file and resumes navigation.
155
+
156
+ ## Route Guards
157
+
158
+ Guards control access to routes:
159
+
160
+ ```typescript
161
+ import { RouteGuard, GuardResult, RouteMatchResult } from '@relax.js/core/routing';
162
+
163
+ class AuthGuard implements RouteGuard {
164
+ check(route: RouteMatchResult): GuardResult {
165
+ if (isAuthenticated()) {
166
+ return GuardResult.Continue; // Check other guards
167
+ }
168
+ navigate('login');
169
+ return GuardResult.Stop; // Prevent navigation
170
+ }
171
+ }
172
+
173
+ const routes: Route[] = [
174
+ { name: 'dashboard', path: '/dashboard', componentTagName: 'dashboard-page', guards: [new AuthGuard()] },
175
+ ];
176
+ ```
177
+
178
+ ### Guard Results
179
+
180
+ | Result | Behavior |
181
+ |--------|----------|
182
+ | `Allow` | Proceed immediately, skip remaining guards |
183
+ | `Continue` | Check next guard (or proceed if none left) |
184
+ | `Stop` | Cancel navigation silently |
185
+ | `Deny` | Cancel navigation and throw `RouteGuardError` |
186
+
187
+ ## Events
188
+
189
+ Navigation dispatches `NavigateRouteEvent` on `document`:
190
+
191
+ ```typescript
192
+ import { NavigateRouteEvent } from '@relax.js/core/routing';
193
+
194
+ document.addEventListener('rlx.navigateRoute', (e: NavigateRouteEvent) => {
195
+ console.log('Route:', e.route.name);
196
+ console.log('Params:', e.routeData);
197
+ console.log('Target:', e.routeTarget ?? 'default');
198
+ });
199
+ ```
200
+
201
+ This event is typed in `HTMLElementEventMap` for full TypeScript support.
202
+
203
+ ## Route Matching
204
+
205
+ For advanced use cases, match routes without navigating:
206
+
207
+ ```typescript
208
+ import { matchRoute, findRouteByUrl, findRouteByName } from '@relax.js/core/routing';
209
+
210
+ // Match by URL
211
+ const result = matchRoute(routes, '/users/john');
212
+ // { route: {...}, params: { userName: 'john' }, urlSegments: ['users', 'john'] }
213
+
214
+ // Match by name
215
+ const result = matchRoute(routes, 'user', { userName: 'john' });
216
+
217
+ // Direct functions
218
+ findRouteByUrl(routes, '/users/john');
219
+ findRouteByName(routes, 'user', { userName: 'john' });
220
+ ```
221
+
222
+ ## Receiving Route Parameters
223
+
224
+ Components rendered by `<r-route-target>` can receive route parameters in two ways.
225
+
226
+ ### loadRoute (async initialization)
227
+
228
+ Implement `loadRoute()` to run async setup before the component is added to the DOM. The component is not visible until `loadRoute()` completes.
229
+
230
+ ```typescript
231
+ import { LoadRoute, RouteData } from '@relax.js/core/routing';
232
+
233
+ class OrderDetail extends HTMLElement implements LoadRoute<{ orderId: number }> {
234
+ private order: Order;
235
+
236
+ async loadRoute(data: { orderId: number }) {
237
+ this.order = await fetchOrder(data.orderId);
238
+ }
239
+
240
+ connectedCallback() {
241
+ this.render(this.order);
242
+ }
243
+ }
244
+ ```
245
+
246
+ ### routeData (typed property)
247
+
248
+ Implement `Routable` to receive parameters as a typed property. The property is optional since it's set by the router after construction.
249
+
250
+ ```typescript
251
+ import { Routable } from '@relax.js/core/routing';
252
+
253
+ class UserProfile extends HTMLElement implements Routable<{ userName: string }> {
254
+ routeData?: { userName: string };
255
+ }
256
+ ```
257
+
258
+ For convention-based usage without the interface, declare `routeData` directly on your component. The router always assigns it regardless.
259
+
260
+ Both can be combined. `loadRoute()` runs first, then `routeData` is assigned.
261
+
262
+ ## Error Handling
263
+
264
+ ```typescript
265
+ import { RouteError, RouteGuardError } from '@relax.js/core/routing';
266
+
267
+ try {
268
+ navigate('unknown-route');
269
+ } catch (e) {
270
+ if (e instanceof RouteGuardError) {
271
+ console.log('Access denied');
272
+ } else if (e instanceof RouteError) {
273
+ console.log('Route not found');
274
+ }
275
+ }
276
+ ```
277
+
278
+ When no route matches, the error message lists all available routes for debugging.
279
+
280
+ Routing errors are reported through the global [error handler](../Errors.md). The error context contains:
281
+
282
+ | Field | Description |
283
+ |-------|-------------|
284
+ | `route` | Route name |
285
+ | `componentTagName` | Custom element tag name |
286
+ | `component` | Component class name (if using class reference) |
287
+ | `routeData` | Parameters extracted from the URL |
288
+
289
+ ## API Reference
290
+
291
+ ### Functions
292
+
293
+ | Function | Description |
294
+ |----------|-------------|
295
+ | `defineRoutes(routes)` | Register routes at startup |
296
+ | `startRouting()` | Initialize router and navigate to current URL |
297
+ | `navigate(nameOrUrl, options?)` | Navigate to a route |
298
+ | `matchRoute(routes, nameOrUrl, params?)` | Match without navigating |
299
+ | `findRouteByUrl(routes, path)` | Match by URL pattern |
300
+ | `findRouteByName(routes, name, params)` | Match by route name |
301
+
302
+ ### Types
303
+
304
+ ```typescript
305
+ type RouteParamType = string | number;
306
+ type RouteData = Record<string, RouteParamType>;
307
+
308
+ interface NavigateOptions {
309
+ params?: Record<string, string | number>;
310
+ target?: string;
311
+ routes?: Route[]; // Override registered routes
312
+ }
313
+
314
+ interface RouteMatchResult {
315
+ route: Route;
316
+ params: RouteData;
317
+ urlSegments: string[];
318
+ }
319
+
320
+ enum GuardResult {
321
+ Allow, // Proceed, skip remaining guards
322
+ Deny, // Throw RouteGuardError
323
+ Continue, // Check next guard
324
+ Stop // Cancel silently
325
+ }
326
+ ```
327
+
328
+ ## Related
329
+
330
+ - [`<r-route-target>`](RoutingTarget.md) - Renders routed components
331
+ - [`<r-link>`](RouteLink.md) - Declarative navigation links
332
+ - [Layouts](layouts.md) - Multiple HTML shells