1
1
import {
2
- effect ,
3
2
inject ,
4
3
Injectable ,
5
4
OnDestroy ,
6
5
PLATFORM_ID ,
7
6
signal ,
8
7
} from '@angular/core' ;
9
- import { currentActionNames } from './currrent -action-names' ;
8
+ import { currentActionNames } from './current -action-names' ;
10
9
import { isPlatformBrowser } from '@angular/common' ;
11
- import { Connection } from '../with-devtools' ;
12
10
import { getState , StateSource } from '@ngrx/signals' ;
13
- import { DevtoolsOptions } from '../devtools-feature' ;
11
+ import { DevtoolsInnerOptions } from './devtools-feature' ;
12
+ import { throwIfNull } from '../../shared/throw-if-null' ;
13
+ import { Connection , StoreRegistry , Tracker } from './models' ;
14
14
15
15
const dummyConnection : Connection = {
16
16
send : ( ) => void true ,
@@ -31,6 +31,7 @@ const dummyConnection: Connection = {
31
31
export class DevtoolsSyncer implements OnDestroy {
32
32
readonly #stores = signal < StoreRegistry > ( { } ) ;
33
33
readonly #isBrowser = isPlatformBrowser ( inject ( PLATFORM_ID ) ) ;
34
+ readonly #trackers = [ ] as Tracker [ ] ;
34
35
#currentId = 1 ;
35
36
36
37
readonly #connection: Connection = this . #isBrowser
@@ -52,37 +53,24 @@ export class DevtoolsSyncer implements OnDestroy {
52
53
'NgRx Toolkit/DevTools: Redux DevTools Extension is not available.'
53
54
) ;
54
55
}
55
-
56
- effect ( ( ) => {
57
- if ( ! this . #connection) {
58
- return ;
59
- }
60
-
61
- const stores = this . #stores( ) ;
62
- const rootState : Record < string , unknown > = { } ;
63
- for ( const name in stores ) {
64
- const { store, options } = stores [ name ] ;
65
- rootState [ name ] = options . map ( getState ( store ) ) ;
66
- }
67
-
68
- const names = Array . from ( currentActionNames ) ;
69
- const type = names . length ? names . join ( ', ' ) : 'Store Update' ;
70
- currentActionNames . clear ( ) ;
71
-
72
- this . #connection. send ( { type } , rootState ) ;
73
- } ) ;
74
56
}
75
57
76
58
ngOnDestroy ( ) : void {
77
59
currentActionNames . clear ( ) ;
78
60
}
79
61
80
- addStore ( name : string , store : StateSource < object > , options : DevtoolsOptions ) {
62
+ addStore (
63
+ name : string ,
64
+ store : StateSource < object > ,
65
+ options : DevtoolsInnerOptions
66
+ ) {
81
67
let storeName = name ;
82
- const names = Object . keys ( this . #stores( ) ) ;
68
+ const names = Object . values ( this . #stores( ) ) . map ( ( store ) => store . name ) ;
83
69
84
70
if ( names . includes ( storeName ) ) {
85
- const { options } = this . #stores( ) [ storeName ] ;
71
+ const { options } = throwIfNull (
72
+ Object . values ( this . #stores( ) ) . find ( ( store ) => store . name === storeName )
73
+ ) ;
86
74
if ( ! options . indexNames ) {
87
75
throw new Error ( `An instance of the store ${ storeName } already exists. \
88
76
Enable automatic indexing via withDevTools('${ storeName } ', { indexNames: true }), or rename it upon instantiation.` ) ;
@@ -92,55 +80,75 @@ Enable automatic indexing via withDevTools('${storeName}', { indexNames: true })
92
80
for ( let i = 1 ; names . includes ( storeName ) ; i ++ ) {
93
81
storeName = `${ name } -${ i } ` ;
94
82
}
95
- const id = this . #currentId++ ;
96
-
83
+ const id = String ( this . #currentId++ ) ;
97
84
this . #stores. update ( ( stores ) => ( {
98
85
...stores ,
99
- [ storeName ] : { store , options , id } ,
86
+ [ id ] : { name : storeName , options } ,
100
87
} ) ) ;
101
88
89
+ const tracker = options . tracker ;
90
+ if ( ! this . #trackers. includes ( tracker ) ) {
91
+ this . #trackers. push ( tracker ) ;
92
+ }
93
+
94
+ tracker . track ( id , store ) ;
95
+ tracker . onChange ( ( ) => this . syncToDevTools ( ) ) ;
96
+
102
97
return id ;
103
98
}
104
99
105
- removeStore ( id : number ) {
106
- this . #stores. update ( ( stores ) => {
107
- return Object . entries ( stores ) . reduce ( ( newStore , [ name , value ] ) => {
108
- if ( value . id === id ) {
109
- return newStore ;
110
- } else {
111
- return { ...newStore , [ name ] : value } ;
100
+ syncToDevTools ( ) {
101
+ const trackerStores = this . #trackers. reduce (
102
+ ( acc , tracker ) => ( { ...acc , ...tracker . getStores ( ) } ) ,
103
+ { } as Record < string , StateSource < object > >
104
+ ) ;
105
+ const rootState = Object . entries ( trackerStores ) . reduce (
106
+ ( acc , [ id , store ] ) => {
107
+ const { options, name } = this . #stores( ) [ id ] ;
108
+ acc [ name ] = options . map ( getState ( store ) ) ;
109
+ return acc ;
110
+ } ,
111
+ { } as Record < string , unknown >
112
+ ) ;
113
+
114
+ const names = Array . from ( currentActionNames ) ;
115
+ const type = names . length ? names . join ( ', ' ) : 'Store Update' ;
116
+ currentActionNames . clear ( ) ;
117
+
118
+ this . #connection. send ( { type } , rootState ) ;
119
+ }
120
+
121
+ removeStore ( id : string ) {
122
+ for ( const tracker of this . #trackers) {
123
+ tracker . removeStore ( id ) ;
124
+ }
125
+ this . #stores. update ( ( stores ) =>
126
+ Object . entries ( stores ) . reduce ( ( newStore , [ storeId , value ] ) => {
127
+ if ( storeId !== id ) {
128
+ newStore [ storeId ] = value ;
112
129
}
113
- } , { } ) ;
114
- } ) ;
130
+ return newStore ;
131
+ } , { } as StoreRegistry )
132
+ ) ;
115
133
}
116
134
117
135
renameStore ( oldName : string , newName : string ) {
118
136
this . #stores. update ( ( stores ) => {
119
- if ( newName in stores ) {
137
+ const storeNames = Object . values ( stores ) . map ( ( store ) => store . name ) ;
138
+ if ( storeNames . includes ( newName ) ) {
120
139
throw new Error (
121
140
`NgRx Toolkit/DevTools: cannot rename from ${ oldName } to ${ newName } . ${ newName } is already assigned to another SignalStore instance.`
122
141
) ;
123
142
}
124
143
125
- const newStore : StoreRegistry = { } ;
126
- for ( const storeName in stores ) {
127
- if ( storeName === oldName ) {
128
- newStore [ newName ] = stores [ oldName ] ;
144
+ return Object . entries ( stores ) . reduce ( ( newStore , [ id , value ] ) => {
145
+ if ( value . name === oldName ) {
146
+ newStore [ id ] = { ...value , name : newName } ;
129
147
} else {
130
- newStore [ storeName ] = stores [ storeName ] ;
148
+ newStore [ id ] = value ;
131
149
}
132
- }
133
-
134
- return newStore ;
150
+ return newStore ;
151
+ } , { } as StoreRegistry ) ;
135
152
} ) ;
136
153
}
137
154
}
138
-
139
- type StoreRegistry = Record <
140
- string ,
141
- {
142
- store : StateSource < object > ;
143
- options : DevtoolsOptions ;
144
- id : number ;
145
- }
146
- > ;
0 commit comments