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