@navios/di-react 0.8.0 → 1.0.0-alpha.3

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 (44) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +14 -67
  3. package/coverage/base.css +224 -0
  4. package/coverage/block-navigation.js +87 -0
  5. package/coverage/clover.xml +270 -0
  6. package/coverage/coverage-final.json +10 -0
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/hooks/index.html +191 -0
  9. package/coverage/hooks/use-container.mts.html +268 -0
  10. package/coverage/hooks/use-invalidate.mts.html +208 -0
  11. package/coverage/hooks/use-optional-service.mts.html +910 -0
  12. package/coverage/hooks/use-scope.mts.html +346 -0
  13. package/coverage/hooks/use-service.mts.html +760 -0
  14. package/coverage/hooks/use-suspense-service.mts.html +784 -0
  15. package/coverage/index.html +131 -0
  16. package/coverage/prettify.css +1 -0
  17. package/coverage/prettify.js +2 -0
  18. package/coverage/providers/container-provider.mts.html +139 -0
  19. package/coverage/providers/context.mts.html +130 -0
  20. package/coverage/providers/index.html +146 -0
  21. package/coverage/providers/scope-provider.mts.html +355 -0
  22. package/coverage/sort-arrow-sprite.png +0 -0
  23. package/coverage/sorter.js +210 -0
  24. package/lib/index.d.mts +3 -57
  25. package/lib/index.d.mts.map +1 -1
  26. package/lib/index.d.ts +3 -57
  27. package/lib/index.d.ts.map +1 -1
  28. package/lib/index.js +59 -72
  29. package/lib/index.js.map +1 -1
  30. package/lib/index.mjs +60 -72
  31. package/lib/index.mjs.map +1 -1
  32. package/package.json +3 -3
  33. package/src/hooks/__tests__/use-container.spec.mts +95 -4
  34. package/src/hooks/__tests__/use-invalidate.spec.mts +10 -146
  35. package/src/hooks/__tests__/use-scope.spec.mts +293 -0
  36. package/src/hooks/__tests__/use-service.spec.mts +2 -2
  37. package/src/hooks/index.mts +1 -1
  38. package/src/hooks/use-container.mts +6 -3
  39. package/src/hooks/use-invalidate.mts +1 -82
  40. package/src/hooks/use-optional-service.mts +23 -32
  41. package/src/hooks/use-service.mts +31 -33
  42. package/src/hooks/use-suspense-service.mts +16 -24
  43. package/src/providers/__tests__/scope-provider.spec.mts +5 -2
  44. package/src/providers/scope-provider.mts +1 -8
package/CHANGELOG.md CHANGED
@@ -5,6 +5,69 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.0-alpha.3] - 2026-01-08
9
+
10
+ ### Added
11
+
12
+ - **Comprehensive Test Suite**: Added unit tests for hooks
13
+ - `use-scope.spec.mts` - Scope hook tests
14
+ - Extended `use-container.spec.mts` with additional test cases
15
+
16
+ ### Fixed
17
+
18
+ - **Improved Warning Message**: Changed `useService` args instability warning from `console.log` to `console.warn`
19
+ - Better semantic meaning for warning messages
20
+ - Improved message formatting and readability
21
+
22
+ ### Dependencies
23
+
24
+ - Updated to support `@navios/di` ^1.0.0-alpha.3
25
+
26
+ ## [0.9.0] - 2025-12-23
27
+
28
+ ### Breaking Changes
29
+
30
+ - **BREAKING**: Removed `useInvalidate` hook
31
+ - This hook relied on the `ServiceLocator` API which was removed in `@navios/di` 0.9.0
32
+ - **Migration**: Use `useInvalidateInstance` instead, which invalidates by instance reference
33
+ - Alternative: Use `container.invalidate(instance)` directly via `useContainer()`
34
+
35
+ ```tsx
36
+ // Before (0.8.x)
37
+ const invalidateUser = useInvalidate(UserService)
38
+ invalidateUser()
39
+
40
+ // After (0.9.0) - Option 1: useInvalidateInstance
41
+ const { data: user } = useService(UserService)
42
+ const invalidateInstance = useInvalidateInstance()
43
+ invalidateInstance(user)
44
+
45
+ // After (0.9.0) - Option 2: Direct container access
46
+ const container = useContainer()
47
+ const { data: user } = useService(UserService)
48
+ await container.invalidate(user)
49
+ ```
50
+
51
+ ### Changed
52
+
53
+ - **Simplified Instance Name Generation**: Refactored all hooks to use the new `container.calculateInstanceName()` method
54
+ - Removed direct access to internal DI components (`getTokenResolver()`, `getNameResolver()`)
55
+ - Cleaner, more maintainable code with less coupling to DI internals
56
+ - **Improved Event Subscription Setup**: Fixed race conditions in invalidation subscription
57
+ - `useOptionalService`: Removed `setTimeout` hack, now properly awaits service fetch before subscribing
58
+ - `useService`: Simplified subscription logic with proper null checks
59
+ - `useSuspenseService`: Streamlined instance name calculation
60
+
61
+ ### Fixed
62
+
63
+ - **Instance Name Generation**: Fixed edge cases where instance names could be incorrectly calculated
64
+ - Now properly handles cases where `calculateInstanceName` returns null
65
+ - Prevents potential subscription errors with undefined instance names
66
+
67
+ ### Dependencies
68
+
69
+ - Updated to support `@navios/di` ^0.9.0 with new unified storage architecture
70
+
8
71
  ## [0.8.0] - 2025-12-21
9
72
 
10
73
  ### Dependencies
package/README.md CHANGED
@@ -282,50 +282,6 @@ function Analytics() {
282
282
  }
283
283
  ```
284
284
 
285
- ### useInvalidate
286
-
287
- Get a function to invalidate a service by its token. When called, this will destroy the current service instance and trigger re-fetch in all components using `useService`/`useSuspenseService` for that token.
288
-
289
- ```tsx
290
- import { useService, useInvalidate } from '@navios/di-react'
291
-
292
- function UserProfile() {
293
- const { data: user } = useService(UserService)
294
- const invalidateUser = useInvalidate(UserService)
295
-
296
- const handleRefresh = () => {
297
- invalidateUser() // All components using UserService will re-fetch
298
- }
299
-
300
- return (
301
- <div>
302
- <span>{user?.name}</span>
303
- <button onClick={handleRefresh}>Refresh</button>
304
- </div>
305
- )
306
- }
307
- ```
308
-
309
- #### With Arguments
310
-
311
- ```tsx
312
- import { useMemo } from 'react'
313
- import { useService, useInvalidate } from '@navios/di-react'
314
-
315
- function UserProfile({ userId }: { userId: string }) {
316
- const args = useMemo(() => ({ userId }), [userId])
317
- const { data: user } = useService(UserToken, args)
318
- const invalidateUser = useInvalidate(UserToken, args)
319
-
320
- return (
321
- <div>
322
- <span>{user?.name}</span>
323
- <button onClick={() => invalidateUser()}>Refresh</button>
324
- </div>
325
- )
326
- }
327
- ```
328
-
329
285
  ### useInvalidateInstance
330
286
 
331
287
  Invalidate a service instance directly without knowing its token.
@@ -441,7 +397,7 @@ function ChildComponent() {
441
397
 
442
398
  ## Service Invalidation
443
399
 
444
- Both `useService` and `useSuspenseService` automatically subscribe to service invalidation events via the DI container's event bus. When a service is invalidated (e.g., via `container.invalidate(service)` or `useInvalidate`), the hooks will automatically:
400
+ Both `useService` and `useSuspenseService` automatically subscribe to service invalidation events via the DI container's event bus. When a service is invalidated (e.g., via `container.invalidate(service)` or `useInvalidateInstance`), the hooks will automatically:
445
401
 
446
402
  1. Clear the cached instance
447
403
  2. Re-fetch the service
@@ -456,11 +412,13 @@ This enables reactive updates when services change, making it easy to implement
456
412
  ```tsx
457
413
  function UserList() {
458
414
  const { data: users } = useService(UserService)
459
- const invalidateUsers = useInvalidate(UserService)
415
+ const invalidateInstance = useInvalidateInstance()
460
416
 
461
417
  const handleCreateUser = async () => {
462
418
  await createUser(newUser)
463
- invalidateUsers() // Automatically refreshes all components using UserService
419
+ if (users) {
420
+ invalidateInstance(users) // Automatically refreshes all components using UserService
421
+ }
464
422
  }
465
423
 
466
424
  return (
@@ -640,18 +598,6 @@ interface UseOptionalServiceResult<T> {
640
598
 
641
599
  Loads a service that may not be registered. Returns `isNotFound: true` when the service doesn't exist instead of throwing an error.
642
600
 
643
- ### useInvalidate
644
-
645
- ```ts
646
- function useInvalidate<T>(token: ClassType): () => Promise<void>
647
- function useInvalidate<T, S>(
648
- token: InjectionToken<T, S>,
649
- args: S extends undefined ? never : unknown,
650
- ): () => Promise<void>
651
- ```
652
-
653
- Returns a function to invalidate a service by its token. When called, destroys the current service instance and triggers re-fetch in all components using `useService`/`useSuspenseService` for that token.
654
-
655
601
  ### useInvalidateInstance
656
602
 
657
603
  ```ts
@@ -739,17 +685,18 @@ Make sure you've wrapped your component with both `Suspense` and an error bounda
739
685
 
740
686
  ### Services not invalidating
741
687
 
742
- Ensure you're using the same token/args combination when invalidating:
688
+ When using `useInvalidateInstance`, make sure you have a reference to the service instance:
743
689
 
744
690
  ```tsx
745
- // ✅ Good - same args
746
- const args = useMemo(() => ({ userId }), [userId])
747
- const { data } = useService(UserToken, args)
748
- const invalidate = useInvalidate(UserToken, args)
691
+ // ✅ Good - invalidate the actual instance
692
+ const { data: user } = useService(UserService)
693
+ const invalidateInstance = useInvalidateInstance()
749
694
 
750
- // Bad - different args
751
- const { data } = useService(UserToken, { userId: '1' })
752
- const invalidate = useInvalidate(UserToken, { userId: '2' }) // Won't invalidate the first one
695
+ const handleRefresh = () => {
696
+ if (user) {
697
+ invalidateInstance(user)
698
+ }
699
+ }
753
700
  ```
754
701
 
755
702
  ## License
@@ -0,0 +1,224 @@
1
+ body, html {
2
+ margin:0; padding: 0;
3
+ height: 100%;
4
+ }
5
+ body {
6
+ font-family: Helvetica Neue, Helvetica, Arial;
7
+ font-size: 14px;
8
+ color:#333;
9
+ }
10
+ .small { font-size: 12px; }
11
+ *, *:after, *:before {
12
+ -webkit-box-sizing:border-box;
13
+ -moz-box-sizing:border-box;
14
+ box-sizing:border-box;
15
+ }
16
+ h1 { font-size: 20px; margin: 0;}
17
+ h2 { font-size: 14px; }
18
+ pre {
19
+ font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20
+ margin: 0;
21
+ padding: 0;
22
+ -moz-tab-size: 2;
23
+ -o-tab-size: 2;
24
+ tab-size: 2;
25
+ }
26
+ a { color:#0074D9; text-decoration:none; }
27
+ a:hover { text-decoration:underline; }
28
+ .strong { font-weight: bold; }
29
+ .space-top1 { padding: 10px 0 0 0; }
30
+ .pad2y { padding: 20px 0; }
31
+ .pad1y { padding: 10px 0; }
32
+ .pad2x { padding: 0 20px; }
33
+ .pad2 { padding: 20px; }
34
+ .pad1 { padding: 10px; }
35
+ .space-left2 { padding-left:55px; }
36
+ .space-right2 { padding-right:20px; }
37
+ .center { text-align:center; }
38
+ .clearfix { display:block; }
39
+ .clearfix:after {
40
+ content:'';
41
+ display:block;
42
+ height:0;
43
+ clear:both;
44
+ visibility:hidden;
45
+ }
46
+ .fl { float: left; }
47
+ @media only screen and (max-width:640px) {
48
+ .col3 { width:100%; max-width:100%; }
49
+ .hide-mobile { display:none!important; }
50
+ }
51
+
52
+ .quiet {
53
+ color: #7f7f7f;
54
+ color: rgba(0,0,0,0.5);
55
+ }
56
+ .quiet a { opacity: 0.7; }
57
+
58
+ .fraction {
59
+ font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60
+ font-size: 10px;
61
+ color: #555;
62
+ background: #E8E8E8;
63
+ padding: 4px 5px;
64
+ border-radius: 3px;
65
+ vertical-align: middle;
66
+ }
67
+
68
+ div.path a:link, div.path a:visited { color: #333; }
69
+ table.coverage {
70
+ border-collapse: collapse;
71
+ margin: 10px 0 0 0;
72
+ padding: 0;
73
+ }
74
+
75
+ table.coverage td {
76
+ margin: 0;
77
+ padding: 0;
78
+ vertical-align: top;
79
+ }
80
+ table.coverage td.line-count {
81
+ text-align: right;
82
+ padding: 0 5px 0 20px;
83
+ }
84
+ table.coverage td.line-coverage {
85
+ text-align: right;
86
+ padding-right: 10px;
87
+ min-width:20px;
88
+ }
89
+
90
+ table.coverage td span.cline-any {
91
+ display: inline-block;
92
+ padding: 0 5px;
93
+ width: 100%;
94
+ }
95
+ .missing-if-branch {
96
+ display: inline-block;
97
+ margin-right: 5px;
98
+ border-radius: 3px;
99
+ position: relative;
100
+ padding: 0 4px;
101
+ background: #333;
102
+ color: yellow;
103
+ }
104
+
105
+ .skip-if-branch {
106
+ display: none;
107
+ margin-right: 10px;
108
+ position: relative;
109
+ padding: 0 4px;
110
+ background: #ccc;
111
+ color: white;
112
+ }
113
+ .missing-if-branch .typ, .skip-if-branch .typ {
114
+ color: inherit !important;
115
+ }
116
+ .coverage-summary {
117
+ border-collapse: collapse;
118
+ width: 100%;
119
+ }
120
+ .coverage-summary tr { border-bottom: 1px solid #bbb; }
121
+ .keyline-all { border: 1px solid #ddd; }
122
+ .coverage-summary td, .coverage-summary th { padding: 10px; }
123
+ .coverage-summary tbody { border: 1px solid #bbb; }
124
+ .coverage-summary td { border-right: 1px solid #bbb; }
125
+ .coverage-summary td:last-child { border-right: none; }
126
+ .coverage-summary th {
127
+ text-align: left;
128
+ font-weight: normal;
129
+ white-space: nowrap;
130
+ }
131
+ .coverage-summary th.file { border-right: none !important; }
132
+ .coverage-summary th.pct { }
133
+ .coverage-summary th.pic,
134
+ .coverage-summary th.abs,
135
+ .coverage-summary td.pct,
136
+ .coverage-summary td.abs { text-align: right; }
137
+ .coverage-summary td.file { white-space: nowrap; }
138
+ .coverage-summary td.pic { min-width: 120px !important; }
139
+ .coverage-summary tfoot td { }
140
+
141
+ .coverage-summary .sorter {
142
+ height: 10px;
143
+ width: 7px;
144
+ display: inline-block;
145
+ margin-left: 0.5em;
146
+ background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147
+ }
148
+ .coverage-summary .sorted .sorter {
149
+ background-position: 0 -20px;
150
+ }
151
+ .coverage-summary .sorted-desc .sorter {
152
+ background-position: 0 -10px;
153
+ }
154
+ .status-line { height: 10px; }
155
+ /* yellow */
156
+ .cbranch-no { background: yellow !important; color: #111; }
157
+ /* dark red */
158
+ .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
159
+ .low .chart { border:1px solid #C21F39 }
160
+ .highlighted,
161
+ .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
162
+ background: #C21F39 !important;
163
+ }
164
+ /* medium red */
165
+ .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
166
+ /* light red */
167
+ .low, .cline-no { background:#FCE1E5 }
168
+ /* light green */
169
+ .high, .cline-yes { background:rgb(230,245,208) }
170
+ /* medium green */
171
+ .cstat-yes { background:rgb(161,215,106) }
172
+ /* dark green */
173
+ .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
174
+ .high .chart { border:1px solid rgb(77,146,33) }
175
+ /* dark yellow (gold) */
176
+ .status-line.medium, .medium .cover-fill { background: #f9cd0b; }
177
+ .medium .chart { border:1px solid #f9cd0b; }
178
+ /* light yellow */
179
+ .medium { background: #fff4c2; }
180
+
181
+ .cstat-skip { background: #ddd; color: #111; }
182
+ .fstat-skip { background: #ddd; color: #111 !important; }
183
+ .cbranch-skip { background: #ddd !important; color: #111; }
184
+
185
+ span.cline-neutral { background: #eaeaea; }
186
+
187
+ .coverage-summary td.empty {
188
+ opacity: .5;
189
+ padding-top: 4px;
190
+ padding-bottom: 4px;
191
+ line-height: 1;
192
+ color: #888;
193
+ }
194
+
195
+ .cover-fill, .cover-empty {
196
+ display:inline-block;
197
+ height: 12px;
198
+ }
199
+ .chart {
200
+ line-height: 0;
201
+ }
202
+ .cover-empty {
203
+ background: white;
204
+ }
205
+ .cover-full {
206
+ border-right: none !important;
207
+ }
208
+ pre.prettyprint {
209
+ border: none !important;
210
+ padding: 0 !important;
211
+ margin: 0 !important;
212
+ }
213
+ .com { color: #999 !important; }
214
+ .ignore-none { color: #999; font-weight: normal; }
215
+
216
+ .wrapper {
217
+ min-height: 100%;
218
+ height: auto !important;
219
+ height: 100%;
220
+ margin: 0 auto -48px;
221
+ }
222
+ .footer, .push {
223
+ height: 48px;
224
+ }
@@ -0,0 +1,87 @@
1
+ /* eslint-disable */
2
+ var jumpToCode = (function init() {
3
+ // Classes of code we would like to highlight in the file view
4
+ var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
5
+
6
+ // Elements to highlight in the file listing view
7
+ var fileListingElements = ['td.pct.low'];
8
+
9
+ // We don't want to select elements that are direct descendants of another match
10
+ var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
11
+
12
+ // Selector that finds elements on the page to which we can jump
13
+ var selector =
14
+ fileListingElements.join(', ') +
15
+ ', ' +
16
+ notSelector +
17
+ missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
18
+
19
+ // The NodeList of matching elements
20
+ var missingCoverageElements = document.querySelectorAll(selector);
21
+
22
+ var currentIndex;
23
+
24
+ function toggleClass(index) {
25
+ missingCoverageElements
26
+ .item(currentIndex)
27
+ .classList.remove('highlighted');
28
+ missingCoverageElements.item(index).classList.add('highlighted');
29
+ }
30
+
31
+ function makeCurrent(index) {
32
+ toggleClass(index);
33
+ currentIndex = index;
34
+ missingCoverageElements.item(index).scrollIntoView({
35
+ behavior: 'smooth',
36
+ block: 'center',
37
+ inline: 'center'
38
+ });
39
+ }
40
+
41
+ function goToPrevious() {
42
+ var nextIndex = 0;
43
+ if (typeof currentIndex !== 'number' || currentIndex === 0) {
44
+ nextIndex = missingCoverageElements.length - 1;
45
+ } else if (missingCoverageElements.length > 1) {
46
+ nextIndex = currentIndex - 1;
47
+ }
48
+
49
+ makeCurrent(nextIndex);
50
+ }
51
+
52
+ function goToNext() {
53
+ var nextIndex = 0;
54
+
55
+ if (
56
+ typeof currentIndex === 'number' &&
57
+ currentIndex < missingCoverageElements.length - 1
58
+ ) {
59
+ nextIndex = currentIndex + 1;
60
+ }
61
+
62
+ makeCurrent(nextIndex);
63
+ }
64
+
65
+ return function jump(event) {
66
+ if (
67
+ document.getElementById('fileSearch') === document.activeElement &&
68
+ document.activeElement != null
69
+ ) {
70
+ // if we're currently focused on the search input, we don't want to navigate
71
+ return;
72
+ }
73
+
74
+ switch (event.which) {
75
+ case 78: // n
76
+ case 74: // j
77
+ goToNext();
78
+ break;
79
+ case 66: // b
80
+ case 75: // k
81
+ case 80: // p
82
+ goToPrevious();
83
+ break;
84
+ }
85
+ };
86
+ })();
87
+ window.addEventListener('keydown', jumpToCode);