@@ -82,13 +82,15 @@ type MutationCacheListener = (event: MutationCacheNotifyEvent) => void
82
82
// CLASS
83
83
84
84
export class MutationCache extends Subscribable < MutationCacheListener > {
85
- #mutations: Map < string , Array < Mutation < any , any , any , any > > >
85
+ #mutations: Set < Mutation < any , any , any , any > >
86
+ #scopes: Map < string , Array < Mutation < any , any , any , any > > >
86
87
#mutationId: number
87
88
88
89
constructor ( public config : MutationCacheConfig = { } ) {
89
90
super ( )
90
- this . #mutations = new Map ( )
91
- this . #mutationId = Date . now ( )
91
+ this . #mutations = new Set ( )
92
+ this . #scopes = new Map ( )
93
+ this . #mutationId = 0
92
94
}
93
95
94
96
build < TData , TError , TVariables , TContext > (
@@ -109,59 +111,84 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
109
111
}
110
112
111
113
add ( mutation : Mutation < any , any , any , any > ) : void {
114
+ this . #mutations. add ( mutation )
112
115
const scope = scopeFor ( mutation )
113
- const mutations = this . #mutations. get ( scope ) ?? [ ]
114
- mutations . push ( mutation )
115
- this . #mutations. set ( scope , mutations )
116
+ if ( typeof scope === 'string' ) {
117
+ const scopedMutations = this . #scopes. get ( scope )
118
+ if ( scopedMutations ) {
119
+ scopedMutations . push ( mutation )
120
+ } else {
121
+ this . #scopes. set ( scope , [ mutation ] )
122
+ }
123
+ }
116
124
this . notify ( { type : 'added' , mutation } )
117
125
}
118
126
119
127
remove ( mutation : Mutation < any , any , any , any > ) : void {
120
- const scope = scopeFor ( mutation )
121
- if ( this . #mutations. has ( scope ) ) {
122
- const mutations = this . #mutations
123
- . get ( scope )
124
- ?. filter ( ( x ) => x !== mutation )
125
- if ( mutations ) {
126
- if ( mutations . length === 0 ) {
127
- this . #mutations. delete ( scope )
128
- } else {
129
- this . #mutations. set ( scope , mutations )
128
+ if ( this . #mutations. delete ( mutation ) ) {
129
+ const scope = scopeFor ( mutation )
130
+ if ( typeof scope === 'string' ) {
131
+ const scopedMutations = this . #scopes. get ( scope )
132
+ if ( scopedMutations ) {
133
+ if ( scopedMutations . length > 1 ) {
134
+ const index = scopedMutations . indexOf ( mutation )
135
+ if ( index !== - 1 ) {
136
+ scopedMutations . splice ( index , 1 )
137
+ }
138
+ } else if ( scopedMutations [ 0 ] === mutation ) {
139
+ this . #scopes. delete ( scope )
140
+ }
130
141
}
131
142
}
132
143
}
133
144
145
+ // Currently we notify the removal even if the mutation was already removed.
146
+ // Consider making this an error or not notifying of the removal depending on the desired semantics.
134
147
this . notify ( { type : 'removed' , mutation } )
135
148
}
136
149
137
150
canRun ( mutation : Mutation < any , any , any , any > ) : boolean {
138
- const firstPendingMutation = this . #mutations
139
- . get ( scopeFor ( mutation ) )
140
- ?. find ( ( m ) => m . state . status === 'pending' )
141
-
142
- // we can run if there is no current pending mutation (start use-case)
143
- // or if WE are the first pending mutation (continue use-case)
144
- return ! firstPendingMutation || firstPendingMutation === mutation
151
+ const scope = scopeFor ( mutation )
152
+ if ( typeof scope === 'string' ) {
153
+ const mutationsWithSameScope = this . #scopes. get ( scope )
154
+ const firstPendingMutation = mutationsWithSameScope ?. find (
155
+ ( m ) => m . state . status === 'pending' ,
156
+ )
157
+ // we can run if there is no current pending mutation (start use-case)
158
+ // or if WE are the first pending mutation (continue use-case)
159
+ return ! firstPendingMutation || firstPendingMutation === mutation
160
+ } else {
161
+ // For unscoped mutations there are never any pending mutations in front of the
162
+ // current mutation
163
+ return true
164
+ }
145
165
}
146
166
147
167
runNext ( mutation : Mutation < any , any , any , any > ) : Promise < unknown > {
148
- const foundMutation = this . #mutations
149
- . get ( scopeFor ( mutation ) )
150
- ?. find ( ( m ) => m !== mutation && m . state . isPaused )
168
+ const scope = scopeFor ( mutation )
169
+ if ( typeof scope === 'string' ) {
170
+ const foundMutation = this . #scopes
171
+ . get ( scope )
172
+ ?. find ( ( m ) => m !== mutation && m . state . isPaused )
151
173
152
- return foundMutation ?. continue ( ) ?? Promise . resolve ( )
174
+ return foundMutation ?. continue ( ) ?? Promise . resolve ( )
175
+ } else {
176
+ return Promise . resolve ( )
177
+ }
153
178
}
154
179
155
180
clear ( ) : void {
156
181
notifyManager . batch ( ( ) => {
157
- this . getAll ( ) . forEach ( ( mutation ) => {
158
- this . remove ( mutation )
182
+ this . #mutations . forEach ( ( mutation ) => {
183
+ this . notify ( { type : 'removed' , mutation } )
159
184
} )
185
+ this . #mutations. clear ( )
186
+ this . #scopes. clear ( )
160
187
} )
161
188
}
162
189
163
190
getAll ( ) : Array < Mutation > {
164
- return [ ... this . #mutations. values ( ) ] . flat ( )
191
+ return Array . from ( this . #mutations)
165
192
}
166
193
167
194
find <
@@ -203,5 +230,5 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
203
230
}
204
231
205
232
function scopeFor ( mutation : Mutation < any , any , any , any > ) {
206
- return mutation . options . scope ?. id ?? String ( mutation . mutationId )
233
+ return mutation . options . scope ?. id
207
234
}
0 commit comments