@@ -82,10 +82,13 @@ function init({
8282
8383 OnyxUtils . initStoreValues ( keys , initialKeyStates , evictableKeys ) ;
8484
85- // Initialize all of our keys with data provided then give green light to any pending connections
86- Promise . all ( [ cache . addEvictableKeysToRecentlyAccessedList ( OnyxUtils . isCollectionKey , OnyxUtils . getAllKeys ) , OnyxUtils . initializeWithDefaultKeyStates ( ) ] ) . then (
87- OnyxUtils . getDeferredInitTask ( ) . resolve ,
88- ) ;
85+ // Initialize all of our keys with data provided then give green light to any pending connections.
86+ // addEvictableKeysToRecentlyAccessedList must run after initializeWithDefaultKeyStates because
87+ // eager cache loading populates the key index (cache.setAllKeys) inside initializeWithDefaultKeyStates,
88+ // and the evictable keys list depends on that index being populated.
89+ OnyxUtils . initializeWithDefaultKeyStates ( )
90+ . then ( ( ) => cache . addEvictableKeysToRecentlyAccessedList ( OnyxUtils . isCollectionKey , OnyxUtils . getAllKeys ) )
91+ . then ( OnyxUtils . getDeferredInitTask ( ) . resolve ) ;
8992}
9093
9194/**
@@ -237,7 +240,13 @@ function merge<TKey extends OnyxKey>(key: TKey, changes: OnyxMergeInput<TKey>):
237240
238241 try {
239242 const validChanges = mergeQueue [ key ] . filter ( ( change ) => {
240- const { isCompatible, existingValueType, newValueType} = utils . checkCompatibilityWithExistingValue ( change , existingValue ) ;
243+ const { isCompatible, existingValueType, newValueType, isEmptyArrayCoercion} = utils . checkCompatibilityWithExistingValue ( change , existingValue ) ;
244+ if ( isEmptyArrayCoercion ) {
245+ // Merging an object into an empty array isn't semantically correct, but we allow it
246+ // in case we accidentally encoded an empty object as an empty array in PHP. If you're
247+ // looking at a bugbot from this message, we're probably missing that key in OnyxKeys::KEYS_REQUIRING_EMPTY_OBJECT
248+ Logger . logAlert ( `[ENSURE_BUGBOT] Onyx merge called on key "${ key } " whose existing value is an empty array. Will coerce to object.` ) ;
249+ }
241250 if ( ! isCompatible ) {
242251 Logger . logAlert ( logMessages . incompatibleUpdateAlert ( key , 'merge' , existingValueType , newValueType ) ) ;
243252 }
@@ -260,8 +269,9 @@ function merge<TKey extends OnyxKey>(key: TKey, changes: OnyxMergeInput<TKey>):
260269 return Promise . resolve ( ) ;
261270 }
262271
263- return OnyxMerge . applyMerge ( key , existingValue , validChanges ) . then ( ( { mergedValue} ) => {
272+ return OnyxMerge . applyMerge ( key , existingValue , validChanges ) . then ( ( { mergedValue, updatePromise } ) => {
264273 OnyxUtils . sendActionToDevTools ( OnyxUtils . METHOD . MERGE , key , changes , mergedValue ) ;
274+ return updatePromise ;
265275 } ) ;
266276 } catch ( error ) {
267277 Logger . logAlert ( `An error occurred while applying merge for key: ${ key } , Error: ${ error } ` ) ;
@@ -338,7 +348,7 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
338348 // to null would cause unknown behavior)
339349 // 2.1 However, if a default key was explicitly set to null, we need to reset it to the default value
340350 for ( const key of allKeys ) {
341- const isKeyToPreserve = keysToPreserve . includes ( key ) ;
351+ const isKeyToPreserve = keysToPreserve . some ( ( preserveKey ) => OnyxUtils . isKeyMatch ( preserveKey , key ) ) ;
342352 const isDefaultKey = key in defaultKeyStates ;
343353
344354 // If the key is being removed or reset to default:
@@ -373,10 +383,20 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
373383 keysToBeClearedFromStorage . push ( key ) ;
374384 }
375385
386+ const updatePromises : Array < Promise < void > > = [ ] ;
387+
388+ // Notify the subscribers for each key/value group so they can receive the new values
389+ for ( const [ key , value ] of Object . entries ( keyValuesToResetIndividually ) ) {
390+ updatePromises . push ( OnyxUtils . scheduleSubscriberUpdate ( key , value ) ) ;
391+ }
392+ for ( const [ key , value ] of Object . entries ( keyValuesToResetAsCollection ) ) {
393+ updatePromises . push ( OnyxUtils . scheduleNotifyCollectionSubscribers ( key , value . newValues , value . oldValues ) ) ;
394+ }
395+
376396 // Exclude RAM-only keys to prevent them from being saved to storage
377397 const defaultKeyValuePairs = Object . entries (
378398 Object . keys ( defaultKeyStates )
379- . filter ( ( key ) => ! keysToPreserve . includes ( key ) && ! OnyxUtils . isRamOnlyKey ( key ) )
399+ . filter ( ( key ) => ! keysToPreserve . some ( ( preserveKey ) => OnyxUtils . isKeyMatch ( preserveKey , key ) ) && ! OnyxUtils . isRamOnlyKey ( key ) )
380400 . reduce ( ( obj : KeyValueMapping , key ) => {
381401 // eslint-disable-next-line no-param-reassign
382402 obj [ key ] = defaultKeyStates [ key ] ;
@@ -391,14 +411,7 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
391411 . then ( ( ) => Storage . multiSet ( defaultKeyValuePairs ) )
392412 . then ( ( ) => {
393413 DevTools . clearState ( keysToPreserve ) ;
394-
395- // Notify the subscribers for each key/value group so they can receive the new values
396- for ( const [ key , value ] of Object . entries ( keyValuesToResetIndividually ) ) {
397- OnyxUtils . keyChanged ( key , value ) ;
398- }
399- for ( const [ key , value ] of Object . entries ( keyValuesToResetAsCollection ) ) {
400- OnyxUtils . keysChanged ( key , value . newValues , value . oldValues ) ;
401- }
414+ return Promise . all ( updatePromises ) ;
402415 } ) ;
403416 } )
404417 . then ( ( ) => undefined ) ;
0 commit comments