@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
@@ -1,237 +1,277 @@
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 'relaxjs/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 'relaxjs/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
-
123
- ```typescript
124
- import { ContainerService, Inject } from 'relaxjs/di';
125
-
126
- @ContainerService({ inject: [] })
127
- class OrderService {
128
- @Inject(LoggerService)
129
- private logger!: LoggerService;
130
-
131
- @Inject('primaryCache') // resolve by string key
132
- private cache!: CacheService;
133
- }
134
- ```
135
-
136
- `@Inject` resolves the dependency immediately when the class is instantiated, so the field is available in all methods (including the constructor body).
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
- ## Integration with Web Components
157
-
158
- ```typescript
159
- @ContainerService({ inject: [UserService] })
160
- class UserProfileComponent extends HTMLElement {
161
- constructor(private userService: UserService) {
162
- super();
163
- }
164
-
165
- connectedCallback() {
166
- this.loadProfile();
167
- }
168
-
169
- async loadProfile() {
170
- const user = await this.userService.getCurrentUser();
171
- this.render(user);
172
- }
173
- }
174
-
175
- customElements.define('user-profile', UserProfileComponent);
176
- ```
177
-
178
- ## Resolving Dependencies
179
-
180
- ```typescript
181
- // By class
182
- const service = container.resolve(MyService);
183
-
184
- // By string key
185
- const cache = container.resolve<CacheService>('primaryCache');
186
- ```
187
-
188
- ## Error Handling
189
-
190
- All DI errors go through the global [`onError`](Errors.md) handler, so you can log, display, or suppress them.
191
-
192
- ### Resolution Errors
193
-
194
- Thrown when `resolve()` cannot find a registered service.
195
-
196
- | Field | Description |
197
- |-------|-------------|
198
- | `service` | The class name or string key that was requested |
199
- | `registeredTypes` | Array of all registered class names |
200
- | `registeredKeys` | Array of all registered string keys |
201
-
202
- ```
203
- RelaxError: Failed to resolve service 'MyService'
204
- { service: 'MyService', registeredTypes: ['LoggerService', 'ConfigService'], registeredKeys: ['primaryCache'] }
205
- ```
206
-
207
- ### Registration Errors
208
-
209
- Thrown during `register()` or `registerByType()` to catch configuration mistakes early.
210
-
211
- | Error | Context | Cause |
212
- |-------|---------|-------|
213
- | Service name collision | `{ service }` | Two different classes registered with the same class name |
214
- | Service key already registered | `{ key, existingClass, newClass }` | A string key is reused for a different class |
215
- | Instance and inject conflict | `{ service }` | Both `instance` and non-empty `inject` provided (`inject` is ignored) |
216
-
217
- ### Suppressing Errors
218
-
219
- ```typescript
220
- import { onError } from 'relaxjs/utils';
221
-
222
- onError((error, ctx) => {
223
- if (error.context.service === 'OptionalPlugin') {
224
- ctx.suppress();
225
- return;
226
- }
227
- console.error(error.message, error.context);
228
- });
229
- ```
230
-
231
- ## Best Practices
232
-
233
- 1. **Register early**: Register all services at application startup
234
- 2. **Prefer constructor injection**: More explicit than property injection
235
- 3. **Use global scope for stateless services**: Like loggers, config
236
- 4. **Use closest scope for request-specific data**: Like user context
237
- 5. **Avoid circular dependencies**: Restructure to use events or mediators
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