@relax.js/core 1.0.4 → 1.0.6

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 +52 -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 +137 -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,333 +0,0 @@
1
- # Architecture Overview
2
-
3
- Relaxjs is a library for building web applications with Web Components. It emphasizes explicit control, direct DOM manipulation, and predictable behavior.
4
-
5
- ## Philosophy
6
-
7
- Unlike modern SPA frameworks, Relaxjs takes an explicit approach:
8
-
9
- - **No virtual DOM**: The real DOM is the source of truth
10
- - **No hidden reactivity**: Changes happen when you explicitly make them
11
- - **No synthetic lifecycle**: Uses native Web Component lifecycle hooks
12
- - **Full developer control**: You always know what triggered what
13
-
14
- ## Core Modules
15
-
16
- ```
17
- @relax.js/core
18
- ├── Routing # SPA navigation with guards and layouts
19
- ├── Forms # Data binding, validation, type conversion
20
- ├── DI # Dependency injection container
21
- ├── HTTP # Fetch wrapper and WebSocket client
22
- ├── i18n # Internationalization with ICU support
23
- ├── Templates # HTML templating with data binding
24
- └── Components # Pre-built UI components
25
- ```
26
-
27
- ## Module Integration
28
-
29
- ### Application Startup
30
-
31
- ```typescript
32
- import { defineRoutes, startRouting } from '@relax.js/core/routing';
33
- import { setLocale } from '@relax.js/core/i18n';
34
- import { serviceCollection } from '@relax.js/core/di';
35
-
36
- class Application extends HTMLElement {
37
- async connectedCallback() {
38
- // 1. Register services
39
- serviceCollection.registerByType(ApiService, { inject: [] });
40
- serviceCollection.registerByType(AuthService, { inject: [ApiService] });
41
-
42
- // 2. Set locale
43
- await setLocale(navigator.language);
44
-
45
- // 3. Define routes
46
- defineRoutes([
47
- { name: 'home', path: '/', componentTagName: 'app-home' },
48
- { name: 'login', path: '/login', componentTagName: 'login-page' }
49
- ]);
50
-
51
- // 4. Start routing
52
- startRouting();
53
- }
54
- }
55
-
56
- customElements.define('app-main', Application);
57
- ```
58
-
59
- ### Component Pattern
60
-
61
- ```typescript
62
- class UserProfile extends HTMLElement {
63
- private form: HTMLFormElement;
64
- private validator: FormValidator;
65
-
66
- connectedCallback() {
67
- this.innerHTML = `
68
- <form>
69
- <input name="name" required>
70
- <input name="email" type="email" required>
71
- <button type="submit">Save</button>
72
- </form>
73
- `;
74
-
75
- this.form = this.querySelector('form')!;
76
- this.validator = new FormValidator(this.form, {
77
- submitCallback: () => this.save()
78
- });
79
-
80
- this.loadUser();
81
- }
82
-
83
- async loadUser() {
84
- const api = container.resolve(ApiService);
85
- const user = await api.get('/user/profile');
86
- setFormData(this.form, user.as<User>());
87
- }
88
-
89
- async save() {
90
- const data = readData(this.form);
91
- const api = container.resolve(ApiService);
92
- await api.put('/user/profile', JSON.stringify(data));
93
- }
94
- }
95
-
96
- customElements.define('user-profile', UserProfile);
97
- ```
98
-
99
- ## Data Flow
100
-
101
- ```
102
- ┌─────────────────────────────────────────────────────────┐
103
- │ User Action │
104
- └─────────────────────────────────────────────────────────┘
105
-
106
-
107
- ┌─────────────────────────────────────────────────────────┐
108
- │ Event Handler │
109
- │ │
110
- │ element.addEventListener('click', (e) => { │
111
- │ // Explicit action │
112
- │ }); │
113
- └─────────────────────────────────────────────────────────┘
114
-
115
-
116
- ┌─────────────────────────────────────────────────────────┐
117
- │ DOM Update │
118
- │ │
119
- │ // Direct manipulation │
120
- │ element.textContent = newValue; │
121
- │ element.setAttribute('data-state', 'active'); │
122
- └─────────────────────────────────────────────────────────┘
123
- ```
124
-
125
- ## Routing Architecture
126
-
127
- ```
128
- ┌──────────────┐ navigate() ┌──────────────────┐
129
- │ User │ ────────────────► │ Router │
130
- │ Action │ │ │
131
- └──────────────┘ │ - Match route │
132
- │ - Check guards │
133
- │ - Update URL │
134
- └────────┬─────────┘
135
-
136
- NavigateRouteEvent
137
-
138
-
139
- ┌──────────────────┐
140
- │ r-route-target│
141
- │ │
142
- │ - Load component│
143
- │ - Pass params │
144
- └──────────────────┘
145
- ```
146
-
147
- ### Multiple Targets
148
-
149
- ```html
150
- <body>
151
- <r-route-target>
152
- <!-- Main content renders here -->
153
- </r-route-target>
154
-
155
- <r-route-target name="modal">
156
- <!-- Modal content renders here -->
157
- </r-route-target>
158
-
159
- <r-route-target name="sidebar">
160
- <!-- Sidebar content renders here -->
161
- </r-route-target>
162
- </body>
163
- ```
164
-
165
- ```typescript
166
- // Navigate to specific target
167
- navigate('userDetails', { params: { id: '123' }, target: 'modal' });
168
- ```
169
-
170
- ## Form Data Flow
171
-
172
- ```
173
- ┌─────────────┐ setFormData() ┌─────────────────────────────────────┐
174
- │ Data │ ──────────────────► │ Form Elements & Custom Components │
175
- │ Object │ │ │
176
- └─────────────┘ │ <input> <select> <textarea> │
177
- │ <r-input> <r-checkbox> (FORM API) │
178
- ▲ └────────┬────────────────────────────┘
179
- │ │
180
- │ readData() User edits
181
- │ │
182
- │ ▼
183
- ┌─────┴───────┐ FormValidator ┌─────────────────┐
184
- │ Updated │ ◄─────────────────── │ Validation │
185
- │ Object │ │ │
186
- └─────────────┘ │ - HTML5 │
187
- │ - Custom rules │
188
- │ - FORM API │
189
- │ components │
190
- └─────────────────┘
191
- ```
192
-
193
- ### Form Component Support
194
-
195
- The form utilities support both native HTML form elements and modern form-associated custom elements (those implementing the FORM API with `ElementInternals`). This means:
196
-
197
- - **Native elements**: `<input>`, `<select>`, `<textarea>` work as expected
198
- - **Custom components**: Web components like `<r-input>`, `<r-checkbox>`, `<r-select>` that use `ElementInternals` and `formAssociated = true` are fully integrated
199
- - **No API differences**: Both types are handled identically by `setFormData()`, `readData()`, `mapFormToClass()`, and `FormValidator`
200
-
201
- ## Template Rendering
202
-
203
- ### Static Templates (html)
204
-
205
- ```typescript
206
- const template = html`
207
- <div class="card">
208
- <h2>${'title'}</h2>
209
- <p>${'description'}</p>
210
- </div>
211
- `;
212
-
213
- // Creates new DOM each time
214
- const fragment = template({ title: 'Hello', description: 'World' });
215
- container.appendChild(fragment);
216
- ```
217
-
218
- ### Updateable Templates (html)
219
-
220
- ```typescript
221
- const template = html`
222
- <div class="user">
223
- <span>{{name}}</span>
224
- <span>{{score|number}}</span>
225
- </div>
226
- `;
227
-
228
- const rendered = template({ name: 'John', score: 42 });
229
- container.appendChild(rendered.fragment);
230
-
231
- // Later, update without recreating DOM
232
- rendered.update({ name: 'Jane', score: 100 });
233
- ```
234
-
235
- ## Service Architecture
236
-
237
- ```
238
- ┌────────────────────────────────────────────────────────┐
239
- │ ServiceCollection │
240
- │ │
241
- │ Register services with metadata: │
242
- │ - Constructor dependencies │
243
- │ - Property injections │
244
- │ - Scope (global/closest) │
245
- └───────────────────────────┬────────────────────────────┘
246
-
247
- │ Configuration
248
-
249
- ┌────────────────────────────────────────────────────────┐
250
- │ ServiceContainer │
251
- │ │
252
- │ Resolve services: │
253
- │ 1. Check cache (for singletons) │
254
- │ 2. Resolve dependencies │
255
- │ 3. Create instance │
256
- │ 4. Inject properties │
257
- │ 5. Cache if global scope │
258
- └────────────────────────────────────────────────────────┘
259
- ```
260
-
261
- ## File Organization
262
-
263
- Recommended project structure:
264
-
265
- ```
266
- src/
267
- ├── components/
268
- │ ├── forms/ # Form-related components
269
- │ ├── lists/ # List/table components
270
- │ └── shared/ # Shared UI components
271
- ├── pages/ # Route components
272
- ├── services/ # Business logic services
273
- ├── guards/ # Route guards
274
- ├── models/ # TypeScript interfaces/types
275
- └── app.ts # Application entry point
276
- ```
277
-
278
- ## Best Practices
279
-
280
- 1. **Use native HTML when possible**: Don't create components for things HTML already does well
281
-
282
- 2. **Keep components small**: Each component should do one thing
283
-
284
- 3. **Form Components Use FORM API**: When building custom form components, use the HTML Form API with `ElementInternals` and `formAssociated = true`. All RelaxJS form utilities automatically support these components without any special handling:
285
-
286
- ```typescript
287
- class CustomCheckbox extends HTMLElement {
288
- static formAssociated = true;
289
-
290
- private internals: ElementInternals;
291
-
292
- constructor() {
293
- super();
294
- this.internals = this.attachInternals();
295
- }
296
-
297
- connectedCallback() {
298
- const checkbox = document.createElement('input');
299
- checkbox.type = 'checkbox';
300
- this.appendChild(checkbox);
301
-
302
- checkbox.addEventListener('change', () => {
303
- this.internals.setFormValue(checkbox.checked ? 'on' : '');
304
- });
305
- }
306
-
307
- get checked() { return (this.querySelector('input') as HTMLInputElement).checked; }
308
- set checked(v) { (this.querySelector('input') as HTMLInputElement).checked = v; }
309
-
310
- get value() { return this.getAttribute('value') || 'on'; }
311
- }
312
-
313
- customElements.define('r-checkbox', CustomCheckbox);
314
- ```
315
-
316
- Once defined, use it seamlessly with form utilities:
317
-
318
- ```typescript
319
- const form = document.querySelector('form');
320
- const data = { termsAccepted: true };
321
- setFormData(form, data); // Works with <r-checkbox name="termsAccepted"></r-checkbox>
322
-
323
- const extracted = readData(form); // Extracts values from custom components
324
- const validator = new FormValidator(form); // Validates custom components
325
- ```
326
-
327
- 3. **Explicit over implicit**: Prefer explicit method calls over magic bindings
328
-
329
- 4. **Type everything**: Use TypeScript interfaces for all data structures
330
-
331
- 5. **CSS variables for theming**: Use semantic variable names
332
-
333
- 6. **Form-associated components**: Use `ElementInternals` for custom form controls
@@ -1,277 +0,0 @@
1
- # Dependency Injection
2
-
3
- A lightweight IoC container for managing service dependencies in your application.
4
-
5
- ## Overview
6
-
7
- The DI system provides:
8
- - Constructor injection
9
- - Property injection
10
- - Singleton and scoped lifetimes
11
- - Decorator-based registration
12
-
13
- ## Quick Start
14
-
15
- ```typescript
16
- import { ContainerService, container } from '@relax.js/core/di';
17
-
18
- @ContainerService()
19
- class LoggerService {
20
- log(message: string) {
21
- console.log(`[LOG] ${message}`);
22
- }
23
- }
24
-
25
- // Resolve and use
26
- const logger = container.resolve(LoggerService);
27
- logger.log('Hello!');
28
- ```
29
-
30
- ## Registration
31
-
32
- Use the `@ContainerService` decorator to register services:
33
-
34
- ```typescript
35
- @ContainerService()
36
- class ConfigService {
37
- apiUrl = '/api/v1';
38
- }
39
-
40
- @ContainerService({ inject: [ConfigService] })
41
- class ApiClient {
42
- constructor(private config: ConfigService) {}
43
-
44
- fetch(path: string) {
45
- return fetch(this.config.apiUrl + path);
46
- }
47
- }
48
- ```
49
-
50
- ### Manual Registration
51
-
52
- For cases where you can't use decorators (e.g. registering an existing instance):
53
-
54
- ```typescript
55
- import { serviceCollection } from '@relax.js/core/di';
56
-
57
- // Register with existing instance
58
- const config = { apiUrl: '/api' };
59
- serviceCollection.register(ConfigService, {
60
- inject: [],
61
- instance: config
62
- });
63
-
64
- // Register with custom key
65
- serviceCollection.register(CacheService, {
66
- key: 'primaryCache',
67
- scope: 'global',
68
- inject: []
69
- });
70
- ```
71
-
72
- ## Scopes
73
-
74
- ### Global (Singleton)
75
-
76
- Same instance returned every time:
77
-
78
- ```typescript
79
- @ContainerService({ scope: 'global' })
80
- class DatabaseConnection {
81
- // ...
82
- }
83
-
84
- const db1 = container.resolve(DatabaseConnection);
85
- const db2 = container.resolve(DatabaseConnection);
86
- // db1 === db2
87
- ```
88
-
89
- ### Closest (Scoped)
90
-
91
- New instance for each container scope:
92
-
93
- ```typescript
94
- @ContainerService({ scope: 'closest' })
95
- class RequestContext {
96
- // ...
97
- }
98
- ```
99
-
100
- ## Constructor Injection
101
-
102
- Dependencies are passed to the constructor in order:
103
-
104
- ```typescript
105
- @ContainerService({
106
- inject: [LoggerService, ConfigService, DatabaseConnection]
107
- })
108
- class UserRepository {
109
- constructor(
110
- private logger: LoggerService,
111
- private config: ConfigService,
112
- private db: DatabaseConnection
113
- ) {}
114
- }
115
- ```
116
-
117
- ## Property Injection
118
-
119
- ### Using the `@Inject` Decorator
120
-
121
- The `@Inject` decorator resolves a dependency and assigns it to a class field.
122
- The service is resolved when the instance is created, so the field is available
123
- in all methods including `connectedCallback` for web components.
124
-
125
- ```typescript
126
- import { ContainerService, Inject } from '@relax.js/core/di';
127
-
128
- @ContainerService({ inject: [] })
129
- class OrderService {
130
- @Inject(LoggerService)
131
- private logger!: LoggerService;
132
-
133
- @Inject('primaryCache') // resolve by string key
134
- private cache!: CacheService;
135
- }
136
- ```
137
-
138
- ### Using the `properties` Option
139
-
140
- Alternatively, declare property injection in the registration options:
141
-
142
- ```typescript
143
- @ContainerService({
144
- inject: [],
145
- properties: {
146
- logger: LoggerService,
147
- cache: 'primaryCache' // string key
148
- }
149
- })
150
- class OrderService {
151
- logger!: LoggerService;
152
- cache!: CacheService;
153
- }
154
- ```
155
-
156
- ## Using Services in Web Components
157
-
158
- Web components use `@Inject` to access services. Do not use `@ContainerService` on
159
- components since the browser creates them with zero constructor arguments.
160
-
161
- ### Property Injection with @Inject
162
-
163
- ```typescript
164
- import { Inject } from '@relax.js/core/di';
165
-
166
- class UserPanel extends HTMLElement {
167
- @Inject(UserService)
168
- private userService!: UserService;
169
-
170
- @Inject(LoggerService)
171
- private logger!: LoggerService;
172
-
173
- connectedCallback() {
174
- // Injected fields are resolved and ready to use
175
- const user = this.userService.getCurrentUser();
176
- this.logger.log('UserPanel connected');
177
- this.render(user);
178
- }
179
- }
180
-
181
- customElements.define('user-panel', UserPanel);
182
- ```
183
-
184
- This works the same way regardless of how the component is created:
185
-
186
- ```typescript
187
- // Created by application code
188
- document.createElement('user-panel');
189
-
190
- // Created by the browser from HTML
191
- // <user-panel></user-panel>
192
- ```
193
-
194
- ### Setup Order
195
-
196
- Register all services before defining custom elements. Services
197
- decorated with `@ContainerService` register themselves when imported,
198
- so import those modules first.
199
-
200
- ```typescript
201
- // main.ts - application entry point
202
-
203
- // 1. Import services (registers them via @ContainerService)
204
- import './services/ApiClient';
205
- import './services/UserService';
206
-
207
- // 2. Register any manual services
208
- serviceCollection.register(ConfigService, {
209
- inject: [],
210
- instance: { apiUrl: '/api/v1' },
211
- });
212
-
213
- // 3. Define custom elements (components can now resolve services)
214
- import { UserPanel } from './components/UserPanel';
215
- customElements.define('user-panel', UserPanel);
216
- ```
217
-
218
- ## Resolving Dependencies
219
-
220
- ```typescript
221
- // By class
222
- const service = container.resolve(MyService);
223
-
224
- // By string key
225
- const cache = container.resolve<CacheService>('primaryCache');
226
- ```
227
-
228
- ## Error Handling
229
-
230
- All DI errors go through the global [`onError`](Errors.md) handler, so you can log, display, or suppress them.
231
-
232
- ### Resolution Errors
233
-
234
- Thrown when `resolve()` cannot find a registered service.
235
-
236
- | Field | Description |
237
- |-------|-------------|
238
- | `service` | The class name or string key that was requested |
239
- | `registeredTypes` | Array of all registered class names |
240
- | `registeredKeys` | Array of all registered string keys |
241
-
242
- ```
243
- RelaxError: Failed to resolve service 'MyService'
244
- { service: 'MyService', registeredTypes: ['LoggerService', 'ConfigService'], registeredKeys: ['primaryCache'] }
245
- ```
246
-
247
- ### Registration Errors
248
-
249
- Thrown during `register()` or `registerByType()` to catch configuration mistakes early.
250
-
251
- | Error | Context | Cause |
252
- |-------|---------|-------|
253
- | Service name collision | `{ service }` | Two different classes registered with the same class name |
254
- | Service key already registered | `{ key, existingClass, newClass }` | A string key is reused for a different class |
255
- | Instance and inject conflict | `{ service }` | Both `instance` and non-empty `inject` provided (`inject` is ignored) |
256
-
257
- ### Suppressing Errors
258
-
259
- ```typescript
260
- import { onError } from '@relax.js/core/utils';
261
-
262
- onError((error, ctx) => {
263
- if (error.context.service === 'OptionalPlugin') {
264
- ctx.suppress();
265
- return;
266
- }
267
- console.error(error.message, error.context);
268
- });
269
- ```
270
-
271
- ## Best Practices
272
-
273
- 1. **Register early**: Register all services at application startup
274
- 2. **Prefer constructor injection**: More explicit than property injection
275
- 3. **Use global scope for stateless services**: Like loggers, config
276
- 4. **Use closest scope for request-specific data**: Like user context
277
- 5. **Avoid circular dependencies**: Restructure to use events or mediators