@@ -82,13 +82,15 @@ type MutationCacheListener = (event: MutationCacheNotifyEvent) => void
8282// CLASS
8383
8484export 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 > > >
8687 #mutationId: number
8788
8889 constructor ( public config : MutationCacheConfig = { } ) {
8990 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
9294 }
9395
9496 build < TData , TError , TVariables , TContext > (
@@ -109,59 +111,84 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
109111 }
110112
111113 add ( mutation : Mutation < any , any , any , any > ) : void {
114+ this . #mutations. add ( mutation )
112115 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+ }
116124 this . notify ( { type : 'added' , mutation } )
117125 }
118126
119127 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+ }
130141 }
131142 }
132143 }
133144
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.
134147 this . notify ( { type : 'removed' , mutation } )
135148 }
136149
137150 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+ }
145165 }
146166
147167 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 )
151173
152- return foundMutation ?. continue ( ) ?? Promise . resolve ( )
174+ return foundMutation ?. continue ( ) ?? Promise . resolve ( )
175+ } else {
176+ return Promise . resolve ( )
177+ }
153178 }
154179
155180 clear ( ) : void {
156181 notifyManager . batch ( ( ) => {
157- this . getAll ( ) . forEach ( ( mutation ) => {
158- this . remove ( mutation )
182+ this . #mutations . forEach ( ( mutation ) => {
183+ this . notify ( { type : 'removed' , mutation } )
159184 } )
185+ this . #mutations. clear ( )
186+ this . #scopes. clear ( )
160187 } )
161188 }
162189
163190 getAll ( ) : Array < Mutation > {
164- return [ ... this . #mutations. values ( ) ] . flat ( )
191+ return Array . from ( this . #mutations)
165192 }
166193
167194 find <
@@ -203,5 +230,5 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
203230}
204231
205232function scopeFor ( mutation : Mutation < any , any , any , any > ) {
206- return mutation . options . scope ?. id ?? String ( mutation . mutationId )
233+ return mutation . options . scope ?. id
207234}
0 commit comments