diff --git a/lib/reducer/storage.ts b/lib/reducer/storage.ts index 91ce1b8..a0e8bcb 100644 --- a/lib/reducer/storage.ts +++ b/lib/reducer/storage.ts @@ -15,7 +15,6 @@ export function defaultNormalize( const keyList = Object.keys(response); const currentStorage = isRecord(cdeebee.storage) ? (cdeebee.storage as Record) : {}; - // Start with existing storage to preserve keys not in response const result = { ...currentStorage } as Record; const keyListToOmit = new Set(); @@ -27,25 +26,21 @@ export function defaultNormalize( continue; } - const isNormalized = isRecord(responseValue) && Object.keys(responseValue).length > 0; + const isNormalized = isRecord(responseValue); + const strategy = strategyList[key as keyof T] ?? 'merge'; if (isNormalized) { - const strategy = strategyList[key as keyof T] ?? 'merge'; const existingValue = key in currentStorage ? (currentStorage[key] as StorageData) : {}; if (strategy === 'replace') { - // Replace: completely replace the value result[key] = responseValue as ResponseValue; } else if (strategy === 'merge') { - // Merge: merge with existing value result[key] = mergeDeepRight(existingValue, responseValue as StorageData) as ResponseValue; } else { - // Unknown strategy: warn and fall back to merge console.warn(`Cdeebee: Unknown strategy "${strategy}" for key "${key}". Skipping normalization.`); result[key] = mergeDeepRight(existingValue, responseValue as StorageData) as ResponseValue; } } else { - // Not a normalized object, store as-is result[key] = responseValue; } } diff --git a/package.json b/package.json index 2450878..72eb25e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@recats/cdeebee", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "description": "React Redux data-logic library", "repository": "git@github.com:recats/cdeebee.git", "author": "recats", diff --git a/tests/lib/reducer/storage.test.ts b/tests/lib/reducer/storage.test.ts index e5e3a98..12e007d 100644 --- a/tests/lib/reducer/storage.test.ts +++ b/tests/lib/reducer/storage.test.ts @@ -240,7 +240,14 @@ describe('defaultNormalize', () => { }); }); - it('should handle empty normalized object', () => { + it('should replace existing data with empty object when using replace strategy', () => { + mockCdeebee.storage = { + userList: { + '1': { id: '1', name: 'John' }, + '2': { id: '2', name: 'Jane' }, + }, + }; + const response = { userList: {}, }; @@ -252,6 +259,68 @@ describe('defaultNormalize', () => { const result = defaultNormalize(mockCdeebee, response, strategyList); expect(result.userList).toEqual({}); + expect(Object.keys(result.userList as Record)).toHaveLength(0); + }); + + it('should preserve existing data when empty object comes with merge strategy', () => { + mockCdeebee.storage = { + userList: { + '1': { id: '1', name: 'John' }, + '2': { id: '2', name: 'Jane' }, + }, + }; + + const response = { + userList: {}, + }; + + const strategyList: CdeebeeListStrategy = { + userList: 'merge', + }; + + const result = defaultNormalize(mockCdeebee, response, strategyList); + + expect(result.userList).toEqual({ + '1': { id: '1', name: 'John' }, + '2': { id: '2', name: 'Jane' }, + }); + expect(Object.keys(result.userList as Record)).toHaveLength(2); + expect((result.userList as Record)['1']).toEqual({ id: '1', name: 'John' }); + expect((result.userList as Record)['2']).toEqual({ id: '2', name: 'Jane' }); + }); + + it('should handle empty objects in single request: merge preserves data, replace erases data', () => { + mockCdeebee.storage = { + userList: { + '1': { id: '1', name: 'John' }, + '2': { id: '2', name: 'Jane' }, + }, + postList: { + '1': { id: '1', title: 'Post 1' }, + '2': { id: '2', title: 'Post 2' }, + }, + }; + + const response = { + userList: {}, + postList: {}, + }; + + const strategyList: CdeebeeListStrategy = { + userList: 'merge', + postList: 'replace', + }; + + const result = defaultNormalize(mockCdeebee, response, strategyList); + + expect(result.userList).toEqual({ + '1': { id: '1', name: 'John' }, + '2': { id: '2', name: 'Jane' }, + }); + expect(Object.keys(result.userList as Record)).toHaveLength(2); + + expect(result.postList).toEqual({}); + expect(Object.keys(result.postList as Record)).toHaveLength(0); }); });