@umituz/react-native-design-system 2.8.7 → 2.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -6
- package/src/device/infrastructure/repositories/LegacyDeviceIdRepository.ts +1 -1
- package/src/device/infrastructure/services/DeviceFeatureService.ts +1 -1
- package/src/exception/infrastructure/services/ExceptionLogger.ts +1 -1
- package/src/exception/infrastructure/storage/ExceptionStore.ts +1 -1
- package/src/exports/filesystem.ts +1 -0
- package/src/exports/storage.ts +1 -0
- package/src/filesystem/domain/constants/FileConstants.ts +20 -0
- package/src/filesystem/domain/entities/File.ts +20 -0
- package/src/filesystem/domain/types/FileTypes.ts +43 -0
- package/src/filesystem/domain/utils/FileUtils.ts +86 -0
- package/src/filesystem/index.ts +23 -0
- package/src/filesystem/infrastructure/services/FileSystemService.ts +45 -0
- package/src/filesystem/infrastructure/services/cache.service.ts +48 -0
- package/src/filesystem/infrastructure/services/directory.service.ts +66 -0
- package/src/filesystem/infrastructure/services/download.constants.ts +6 -0
- package/src/filesystem/infrastructure/services/download.service.ts +74 -0
- package/src/filesystem/infrastructure/services/download.types.ts +7 -0
- package/src/filesystem/infrastructure/services/encoding.service.ts +25 -0
- package/src/filesystem/infrastructure/services/file-info.service.ts +52 -0
- package/src/filesystem/infrastructure/services/file-manager.service.ts +81 -0
- package/src/filesystem/infrastructure/services/file-path.service.ts +22 -0
- package/src/filesystem/infrastructure/services/file-reader.service.ts +52 -0
- package/src/filesystem/infrastructure/services/file-writer.service.ts +32 -0
- package/src/filesystem/infrastructure/utils/blob.utils.ts +20 -0
- package/src/image/infrastructure/services/ImageStorageService.ts +1 -1
- package/src/index.ts +9 -0
- package/src/molecules/alerts/AlertStore.ts +1 -1
- package/src/molecules/calendar/infrastructure/storage/EventActions.ts +1 -1
- package/src/molecules/calendar/infrastructure/stores/storageAdapter.ts +1 -1
- package/src/offline/infrastructure/storage/OfflineStore.ts +1 -1
- package/src/onboarding/infrastructure/storage/OnboardingStore.ts +2 -2
- package/src/onboarding/infrastructure/storage/__tests__/OnboardingStore.test.ts +1 -1
- package/src/onboarding/infrastructure/storage/actions/answerActions.ts +1 -1
- package/src/onboarding/infrastructure/storage/actions/storageHelpers.ts +1 -1
- package/src/storage/README.md +185 -0
- package/src/storage/__tests__/integration.test.ts +391 -0
- package/src/storage/__tests__/mocks/asyncStorage.mock.ts +52 -0
- package/src/storage/__tests__/performance.test.tsx +352 -0
- package/src/storage/__tests__/setup.ts +63 -0
- package/src/storage/application/README.md +158 -0
- package/src/storage/application/ports/IStorageRepository.ts +61 -0
- package/src/storage/application/ports/README.md +127 -0
- package/src/storage/cache/README.md +154 -0
- package/src/storage/cache/__tests__/PerformanceAndMemory.test.ts +387 -0
- package/src/storage/cache/__tests__/setup.ts +19 -0
- package/src/storage/cache/domain/Cache.ts +146 -0
- package/src/storage/cache/domain/CacheManager.md +83 -0
- package/src/storage/cache/domain/CacheManager.ts +48 -0
- package/src/storage/cache/domain/CacheStatsTracker.md +169 -0
- package/src/storage/cache/domain/CacheStatsTracker.ts +49 -0
- package/src/storage/cache/domain/CachedValue.md +97 -0
- package/src/storage/cache/domain/ErrorHandler.md +99 -0
- package/src/storage/cache/domain/ErrorHandler.ts +42 -0
- package/src/storage/cache/domain/PatternMatcher.md +122 -0
- package/src/storage/cache/domain/PatternMatcher.ts +30 -0
- package/src/storage/cache/domain/README.md +118 -0
- package/src/storage/cache/domain/__tests__/Cache.test.ts +293 -0
- package/src/storage/cache/domain/__tests__/CacheManager.test.ts +276 -0
- package/src/storage/cache/domain/__tests__/ErrorHandler.test.ts +303 -0
- package/src/storage/cache/domain/__tests__/PatternMatcher.test.ts +261 -0
- package/src/storage/cache/domain/strategies/EvictionStrategy.ts +9 -0
- package/src/storage/cache/domain/strategies/FIFOStrategy.ts +12 -0
- package/src/storage/cache/domain/strategies/LFUStrategy.ts +22 -0
- package/src/storage/cache/domain/strategies/LRUStrategy.ts +22 -0
- package/src/storage/cache/domain/strategies/README.md +117 -0
- package/src/storage/cache/domain/strategies/TTLStrategy.ts +23 -0
- package/src/storage/cache/domain/strategies/__tests__/EvictionStrategies.test.ts +293 -0
- package/src/storage/cache/domain/types/Cache.ts +28 -0
- package/src/storage/cache/domain/types/README.md +107 -0
- package/src/storage/cache/index.ts +28 -0
- package/src/storage/cache/infrastructure/README.md +126 -0
- package/src/storage/cache/infrastructure/TTLCache.ts +103 -0
- package/src/storage/cache/infrastructure/__tests__/TTLCache.test.ts +303 -0
- package/src/storage/cache/presentation/README.md +123 -0
- package/src/storage/cache/presentation/__tests__/ReactHooks.test.ts +514 -0
- package/src/storage/cache/presentation/useCache.ts +76 -0
- package/src/storage/cache/presentation/useCachedValue.ts +88 -0
- package/src/storage/cache/types.d.ts +3 -0
- package/src/storage/domain/README.md +128 -0
- package/src/storage/domain/constants/CacheDefaults.ts +64 -0
- package/src/storage/domain/constants/README.md +105 -0
- package/src/storage/domain/entities/CachedValue.ts +86 -0
- package/src/storage/domain/entities/README.md +109 -0
- package/src/storage/domain/entities/StorageResult.ts +75 -0
- package/src/storage/domain/entities/__tests__/CachedValue.test.ts +149 -0
- package/src/storage/domain/entities/__tests__/StorageResult.test.ts +122 -0
- package/src/storage/domain/errors/README.md +126 -0
- package/src/storage/domain/errors/StorageError.ts +81 -0
- package/src/storage/domain/errors/__tests__/StorageError.test.ts +127 -0
- package/src/storage/domain/factories/README.md +138 -0
- package/src/storage/domain/factories/StoreFactory.ts +59 -0
- package/src/storage/domain/types/README.md +522 -0
- package/src/storage/domain/types/Store.ts +44 -0
- package/src/storage/domain/utils/CacheKeyGenerator.ts +66 -0
- package/src/storage/domain/utils/README.md +127 -0
- package/src/storage/domain/utils/__tests__/devUtils.test.ts +97 -0
- package/src/storage/domain/utils/devUtils.ts +37 -0
- package/src/storage/domain/value-objects/README.md +120 -0
- package/src/storage/domain/value-objects/StorageKey.ts +60 -0
- package/src/storage/index.ts +175 -0
- package/src/storage/infrastructure/README.md +165 -0
- package/src/storage/infrastructure/adapters/README.md +175 -0
- package/src/storage/infrastructure/adapters/StorageService.md +103 -0
- package/src/storage/infrastructure/adapters/StorageService.ts +49 -0
- package/src/storage/infrastructure/repositories/AsyncStorageRepository.ts +98 -0
- package/src/storage/infrastructure/repositories/BaseStorageOperations.ts +100 -0
- package/src/storage/infrastructure/repositories/BatchStorageOperations.ts +42 -0
- package/src/storage/infrastructure/repositories/README.md +121 -0
- package/src/storage/infrastructure/repositories/StringStorageOperations.ts +44 -0
- package/src/storage/infrastructure/repositories/__tests__/AsyncStorageRepository.test.ts +170 -0
- package/src/storage/infrastructure/repositories/__tests__/BaseStorageOperations.test.ts +201 -0
- package/src/storage/presentation/README.md +181 -0
- package/src/storage/presentation/hooks/CacheStorageOperations.ts +94 -0
- package/src/storage/presentation/hooks/README.md +128 -0
- package/src/storage/presentation/hooks/__tests__/usePersistentCache.test.ts +405 -0
- package/src/storage/presentation/hooks/__tests__/useStorage.test.ts +247 -0
- package/src/storage/presentation/hooks/__tests__/useStorageState.test.ts +293 -0
- package/src/storage/presentation/hooks/useCacheState.ts +53 -0
- package/src/storage/presentation/hooks/usePersistentCache.ts +154 -0
- package/src/storage/presentation/hooks/useStorage.ts +102 -0
- package/src/storage/presentation/hooks/useStorageState.ts +71 -0
- package/src/storage/presentation/hooks/useStore.ts +15 -0
- package/src/storage/types/README.md +103 -0
- package/src/theme/infrastructure/globalThemeStore.ts +1 -1
- package/src/theme/infrastructure/storage/ThemeStorage.ts +1 -1
- package/src/theme/infrastructure/stores/themeStore.ts +1 -1
- package/src/utilities/sharing/infrastructure/services/SharingService.ts +1 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CachedValue Entity Tests
|
|
3
|
+
*
|
|
4
|
+
* Unit tests for CachedValue entity
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
createCachedValue,
|
|
9
|
+
isCacheExpired,
|
|
10
|
+
getRemainingTTL,
|
|
11
|
+
getCacheAge
|
|
12
|
+
} from '../../domain/entities/CachedValue';
|
|
13
|
+
|
|
14
|
+
describe('CachedValue Entity', () => {
|
|
15
|
+
const mockNow = 1000000;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
jest.spyOn(Date, 'now').mockReturnValue(mockNow);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
jest.restoreAllMocks();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('createCachedValue', () => {
|
|
26
|
+
it('should create cached value with default values', () => {
|
|
27
|
+
const data = { test: 'value' };
|
|
28
|
+
const cached = createCachedValue(data);
|
|
29
|
+
|
|
30
|
+
expect(cached.value).toBe(data);
|
|
31
|
+
expect(cached.timestamp).toBe(mockNow);
|
|
32
|
+
expect(cached.ttl).toBeDefined();
|
|
33
|
+
expect(cached.version).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should create cached value with custom TTL', () => {
|
|
37
|
+
const data = { test: 'value' };
|
|
38
|
+
const ttl = 60000; // 1 minute
|
|
39
|
+
const cached = createCachedValue(data, ttl);
|
|
40
|
+
|
|
41
|
+
expect(cached.value).toBe(data);
|
|
42
|
+
expect(cached.timestamp).toBe(mockNow);
|
|
43
|
+
expect(cached.ttl).toBe(ttl);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should create cached value with version', () => {
|
|
47
|
+
const data = { test: 'value' };
|
|
48
|
+
const version = 2;
|
|
49
|
+
const cached = createCachedValue(data, undefined, version);
|
|
50
|
+
|
|
51
|
+
expect(cached.value).toBe(data);
|
|
52
|
+
expect(cached.timestamp).toBe(mockNow);
|
|
53
|
+
expect(cached.version).toBe(version);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('isCacheExpired', () => {
|
|
58
|
+
it('should return false for fresh cache', () => {
|
|
59
|
+
const data = { test: 'value' };
|
|
60
|
+
const ttl = 60000; // 1 minute
|
|
61
|
+
const cached = createCachedValue(data, ttl);
|
|
62
|
+
|
|
63
|
+
expect(isCacheExpired(cached)).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should return true for expired cache', () => {
|
|
67
|
+
const data = { test: 'value' };
|
|
68
|
+
const ttl = 1000; // 1 second
|
|
69
|
+
const cached = createCachedValue(data, ttl, mockNow - 2000); // Created 2 seconds ago
|
|
70
|
+
|
|
71
|
+
expect(isCacheExpired(cached)).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should handle version mismatch', () => {
|
|
75
|
+
const data = { test: 'value' };
|
|
76
|
+
const cached = createCachedValue(data, 60000, 1);
|
|
77
|
+
const currentVersion = 2;
|
|
78
|
+
|
|
79
|
+
expect(isCacheExpired(cached, currentVersion)).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should return false when version matches', () => {
|
|
83
|
+
const data = { test: 'value' };
|
|
84
|
+
const cached = createCachedValue(data, 60000, 1);
|
|
85
|
+
const currentVersion = 1;
|
|
86
|
+
|
|
87
|
+
expect(isCacheExpired(cached, currentVersion)).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('getRemainingTTL', () => {
|
|
92
|
+
it('should return remaining TTL for fresh cache', () => {
|
|
93
|
+
const data = { test: 'value' };
|
|
94
|
+
const ttl = 60000; // 1 minute
|
|
95
|
+
const cached = createCachedValue(data, ttl, mockNow - 30000); // Created 30 seconds ago
|
|
96
|
+
|
|
97
|
+
const remaining = getRemainingTTL(cached);
|
|
98
|
+
expect(remaining).toBe(30000); // 30 seconds remaining
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should return 0 for expired cache', () => {
|
|
102
|
+
const data = { test: 'value' };
|
|
103
|
+
const ttl = 1000; // 1 second
|
|
104
|
+
const cached = createCachedValue(data, ttl, mockNow - 2000); // Created 2 seconds ago
|
|
105
|
+
|
|
106
|
+
const remaining = getRemainingTTL(cached);
|
|
107
|
+
expect(remaining).toBe(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should handle missing TTL', () => {
|
|
111
|
+
const data = { test: 'value' };
|
|
112
|
+
const cached = createCachedValue(data);
|
|
113
|
+
|
|
114
|
+
const remaining = getRemainingTTL(cached);
|
|
115
|
+
expect(remaining).toBeGreaterThan(0);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('getCacheAge', () => {
|
|
120
|
+
it('should return correct age', () => {
|
|
121
|
+
const data = { test: 'value' };
|
|
122
|
+
const age = 30000; // 30 seconds
|
|
123
|
+
const cached = createCachedValue(data, undefined, undefined, mockNow - age);
|
|
124
|
+
|
|
125
|
+
const cacheAge = getCacheAge(cached);
|
|
126
|
+
expect(cacheAge).toBe(age);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should return 0 for new cache', () => {
|
|
130
|
+
const data = { test: 'value' };
|
|
131
|
+
const cached = createCachedValue(data);
|
|
132
|
+
|
|
133
|
+
const cacheAge = getCacheAge(cached);
|
|
134
|
+
expect(cacheAge).toBe(0);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('Immutability', () => {
|
|
139
|
+
it('should create immutable cached value', () => {
|
|
140
|
+
const data = { test: 'value' };
|
|
141
|
+
const cached = createCachedValue(data);
|
|
142
|
+
|
|
143
|
+
// Attempt to modify
|
|
144
|
+
(cached as any).value = { modified: true };
|
|
145
|
+
|
|
146
|
+
expect(cached.value).toEqual(data);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StorageResult Entity Tests
|
|
3
|
+
*
|
|
4
|
+
* Unit tests for StorageResult entity
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { success, failure, unwrap, map, isSuccess, isFailure } from '../../domain/entities/StorageResult';
|
|
8
|
+
import { StorageError } from '../../domain/errors/StorageError';
|
|
9
|
+
|
|
10
|
+
describe('StorageResult Entity', () => {
|
|
11
|
+
describe('success', () => {
|
|
12
|
+
it('should create a successful result', () => {
|
|
13
|
+
const data = { test: 'value' };
|
|
14
|
+
const result = success(data);
|
|
15
|
+
|
|
16
|
+
expect(result.success).toBe(true);
|
|
17
|
+
expect(result.data).toBe(data);
|
|
18
|
+
expect(result.error).toBeUndefined();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('failure', () => {
|
|
23
|
+
it('should create a failure result', () => {
|
|
24
|
+
const error = new StorageError('test-key', new Error('Test error'));
|
|
25
|
+
const defaultValue = 'default';
|
|
26
|
+
const result = failure(error, defaultValue);
|
|
27
|
+
|
|
28
|
+
expect(result.success).toBe(false);
|
|
29
|
+
expect(result.data).toBe(defaultValue);
|
|
30
|
+
expect(result.error).toBe(error);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('unwrap', () => {
|
|
35
|
+
it('should return data for successful result', () => {
|
|
36
|
+
const data = { test: 'value' };
|
|
37
|
+
const result = success(data);
|
|
38
|
+
const defaultValue = 'default';
|
|
39
|
+
|
|
40
|
+
const unwrapped = unwrap(result, defaultValue);
|
|
41
|
+
|
|
42
|
+
expect(unwrapped).toBe(data);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should return default value for failure result', () => {
|
|
46
|
+
const error = new StorageError('test-key', new Error('Test error'));
|
|
47
|
+
const defaultValue = 'default';
|
|
48
|
+
const result = failure(error, defaultValue);
|
|
49
|
+
|
|
50
|
+
const unwrapped = unwrap(result, defaultValue);
|
|
51
|
+
|
|
52
|
+
expect(unwrapped).toBe(defaultValue);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('map', () => {
|
|
57
|
+
it('should map successful result', () => {
|
|
58
|
+
const data = { count: 1 };
|
|
59
|
+
const result = success(data);
|
|
60
|
+
const mapper = (value: typeof data) => ({ ...value, count: value.count + 1 });
|
|
61
|
+
|
|
62
|
+
const mapped = map(result, mapper);
|
|
63
|
+
|
|
64
|
+
expect(mapped.success).toBe(true);
|
|
65
|
+
expect(mapped.data).toEqual({ count: 2 });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should not map failure result', () => {
|
|
69
|
+
const error = new StorageError('test-key', new Error('Test error'));
|
|
70
|
+
const defaultValue = { count: 0 };
|
|
71
|
+
const result = failure(error, defaultValue);
|
|
72
|
+
const mapper = jest.fn();
|
|
73
|
+
|
|
74
|
+
const mapped = map(result, mapper);
|
|
75
|
+
|
|
76
|
+
expect(mapped.success).toBe(false);
|
|
77
|
+
expect(mapper).not.toHaveBeenCalled();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('isSuccess', () => {
|
|
82
|
+
it('should return true for successful result', () => {
|
|
83
|
+
const result = success('test');
|
|
84
|
+
expect(isSuccess(result)).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should return false for failure result', () => {
|
|
88
|
+
const error = new StorageError('test-key', new Error('Test error'));
|
|
89
|
+
const result = failure(error);
|
|
90
|
+
expect(isSuccess(result)).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('isFailure', () => {
|
|
95
|
+
it('should return false for successful result', () => {
|
|
96
|
+
const result = success('test');
|
|
97
|
+
expect(isFailure(result)).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return true for failure result', () => {
|
|
101
|
+
const error = new StorageError('test-key', new Error('Test error'));
|
|
102
|
+
const result = failure(error);
|
|
103
|
+
expect(isFailure(result)).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Type Safety', () => {
|
|
108
|
+
it('should maintain type information', () => {
|
|
109
|
+
interface TestData {
|
|
110
|
+
id: number;
|
|
111
|
+
name: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const data: TestData = { id: 1, name: 'test' };
|
|
115
|
+
const result = success<TestData>(data);
|
|
116
|
+
|
|
117
|
+
// TypeScript should infer the type correctly
|
|
118
|
+
const typedData: TestData = result.data;
|
|
119
|
+
expect(typedData).toEqual(data);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Storage Errors
|
|
2
|
+
|
|
3
|
+
Error classes and error handling strategies for storage operations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Hierarchical error classes for storage operations with context preservation. Located at `src/domain/errors/`.
|
|
8
|
+
|
|
9
|
+
## Strategies
|
|
10
|
+
|
|
11
|
+
### Error Hierarchy
|
|
12
|
+
- Use StorageError as base class for all storage errors
|
|
13
|
+
- Create specific error types for different operations
|
|
14
|
+
- Preserve error context (key, operation, cause)
|
|
15
|
+
- Enable error type checking with instanceof
|
|
16
|
+
|
|
17
|
+
### Error Categories
|
|
18
|
+
- StorageReadError for read failures
|
|
19
|
+
- StorageWriteError for write failures
|
|
20
|
+
- StorageDeleteError for delete failures
|
|
21
|
+
- StorageSerializationError for serialization failures
|
|
22
|
+
- StorageDeserializationError for parsing failures
|
|
23
|
+
|
|
24
|
+
### Error Handling Strategy
|
|
25
|
+
- Use Result pattern instead of throwing exceptions
|
|
26
|
+
- Preserve original error as cause property
|
|
27
|
+
- Include error codes for programmatic handling
|
|
28
|
+
- Log errors with full context
|
|
29
|
+
|
|
30
|
+
### Error Recovery
|
|
31
|
+
- Implement retry logic for transient failures
|
|
32
|
+
- Provide fallback values for graceful degradation
|
|
33
|
+
- Clear corrupted data when appropriate
|
|
34
|
+
- Alert monitoring systems for critical errors
|
|
35
|
+
|
|
36
|
+
## Restrictions
|
|
37
|
+
|
|
38
|
+
### Error Creation
|
|
39
|
+
- DO NOT throw generic Error class
|
|
40
|
+
- DO NOT lose error context when wrapping
|
|
41
|
+
- DO NOT create error types without clear purpose
|
|
42
|
+
- DO NOT use strings for error codes
|
|
43
|
+
|
|
44
|
+
### Error Handling
|
|
45
|
+
- DO NOT catch and ignore errors silently
|
|
46
|
+
- DO NOT suppress error logging in production
|
|
47
|
+
- DO NOT retry indefinitely on persistent failures
|
|
48
|
+
- DO NOT expose sensitive data in error messages
|
|
49
|
+
|
|
50
|
+
### Error Types
|
|
51
|
+
- DO NOT mix different error categories
|
|
52
|
+
- DO NOT create circular error hierarchies
|
|
53
|
+
- DO NOT omit error code from custom errors
|
|
54
|
+
- DO NOT cause infinite error loops
|
|
55
|
+
|
|
56
|
+
## Rules
|
|
57
|
+
|
|
58
|
+
### Error Base Class
|
|
59
|
+
- MUST extend Error class
|
|
60
|
+
- MUST include code property (string)
|
|
61
|
+
- MUST preserve stack trace
|
|
62
|
+
- MUST accept cause parameter for wrapping
|
|
63
|
+
- MUST include message parameter
|
|
64
|
+
|
|
65
|
+
### Specific Error Types
|
|
66
|
+
- MUST extend StorageError base class
|
|
67
|
+
- MUST include key property for storage errors
|
|
68
|
+
- MUST include cause property when wrapping
|
|
69
|
+
- MUST use specific error codes
|
|
70
|
+
- MUST set appropriate error name
|
|
71
|
+
|
|
72
|
+
### Error Codes
|
|
73
|
+
- MUST use format: CATEGORY_ERROR (e.g., STORAGE_READ_ERROR)
|
|
74
|
+
- MUST be unique across error types
|
|
75
|
+
- MUST be descriptive and searchable
|
|
76
|
+
- MUST follow naming convention
|
|
77
|
+
- MUST be documented
|
|
78
|
+
|
|
79
|
+
### Error Context
|
|
80
|
+
- MUST include storage key in storage errors
|
|
81
|
+
- MUST include original operation type
|
|
82
|
+
- MUST attach cause error when wrapping
|
|
83
|
+
- MUST preserve stack trace
|
|
84
|
+
- MUST include timestamp for debugging
|
|
85
|
+
|
|
86
|
+
### Error Handling Patterns
|
|
87
|
+
- MUST use try-catch for all storage operations
|
|
88
|
+
- MUST check error types with instanceof
|
|
89
|
+
- MUST log errors with full context
|
|
90
|
+
- MUST provide error recovery where possible
|
|
91
|
+
- MUST not expose sensitive data
|
|
92
|
+
|
|
93
|
+
### Custom Error Creation
|
|
94
|
+
- MUST extend StorageError for custom errors
|
|
95
|
+
- MUST provide unique error code
|
|
96
|
+
- MUST include all required context
|
|
97
|
+
- MUST document error purpose
|
|
98
|
+
- MUST follow naming conventions
|
|
99
|
+
|
|
100
|
+
### Error Logging
|
|
101
|
+
- MUST log error code and message
|
|
102
|
+
- MUST log error context (key, operation)
|
|
103
|
+
- MUST log cause error if present
|
|
104
|
+
- MUST log stack trace in development
|
|
105
|
+
- MUST not log sensitive data
|
|
106
|
+
|
|
107
|
+
### Error Testing
|
|
108
|
+
- MUST test error creation
|
|
109
|
+
- MUST test error type checking
|
|
110
|
+
- MUST test error context preservation
|
|
111
|
+
- MUST test error logging
|
|
112
|
+
- MUST test error recovery logic
|
|
113
|
+
|
|
114
|
+
### Type Safety
|
|
115
|
+
- MUST use instanceof for type checking
|
|
116
|
+
- MUST use type guards where appropriate
|
|
117
|
+
- MUST enforce error codes as literal types
|
|
118
|
+
- MUST provide TypeScript types
|
|
119
|
+
- MUST not use `any` for error types
|
|
120
|
+
|
|
121
|
+
### Error Recovery
|
|
122
|
+
- MUST implement retry limits
|
|
123
|
+
- MUST provide fallback strategies
|
|
124
|
+
- MUST clear corrupted data when detected
|
|
125
|
+
- MUST alert on critical failures
|
|
126
|
+
- MUST document recovery behavior
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage Error Types
|
|
3
|
+
*
|
|
4
|
+
* Domain-Driven Design: Domain errors for storage operations
|
|
5
|
+
* Typed errors for better error handling and debugging
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base Storage Error
|
|
10
|
+
*/
|
|
11
|
+
export class StorageError extends Error {
|
|
12
|
+
constructor(message: string, public readonly key?: string) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'StorageError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Storage Read Error
|
|
20
|
+
*/
|
|
21
|
+
export class StorageReadError extends StorageError {
|
|
22
|
+
public readonly cause?: unknown;
|
|
23
|
+
|
|
24
|
+
constructor(key: string, cause?: unknown) {
|
|
25
|
+
super(`Failed to read from storage: ${key}`, key);
|
|
26
|
+
this.name = 'StorageReadError';
|
|
27
|
+
this.cause = cause;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Storage Write Error
|
|
33
|
+
*/
|
|
34
|
+
export class StorageWriteError extends StorageError {
|
|
35
|
+
public readonly cause?: unknown;
|
|
36
|
+
|
|
37
|
+
constructor(key: string, cause?: unknown) {
|
|
38
|
+
super(`Failed to write to storage: ${key}`, key);
|
|
39
|
+
this.name = 'StorageWriteError';
|
|
40
|
+
this.cause = cause;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Storage Delete Error
|
|
46
|
+
*/
|
|
47
|
+
export class StorageDeleteError extends StorageError {
|
|
48
|
+
public readonly cause?: unknown;
|
|
49
|
+
|
|
50
|
+
constructor(key: string, cause?: unknown) {
|
|
51
|
+
super(`Failed to delete from storage: ${key}`, key);
|
|
52
|
+
this.name = 'StorageDeleteError';
|
|
53
|
+
this.cause = cause;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Storage Serialization Error
|
|
59
|
+
*/
|
|
60
|
+
export class StorageSerializationError extends StorageError {
|
|
61
|
+
public readonly cause?: unknown;
|
|
62
|
+
|
|
63
|
+
constructor(key: string, cause?: unknown) {
|
|
64
|
+
super(`Failed to serialize data for key: ${key}`, key);
|
|
65
|
+
this.name = 'StorageSerializationError';
|
|
66
|
+
this.cause = cause;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Storage Deserialization Error
|
|
72
|
+
*/
|
|
73
|
+
export class StorageDeserializationError extends StorageError {
|
|
74
|
+
public readonly cause?: unknown;
|
|
75
|
+
|
|
76
|
+
constructor(key: string, cause?: unknown) {
|
|
77
|
+
super(`Failed to deserialize data for key: ${key}`, key);
|
|
78
|
+
this.name = 'StorageDeserializationError';
|
|
79
|
+
this.cause = cause;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StorageError Tests
|
|
3
|
+
*
|
|
4
|
+
* Unit tests for StorageError classes
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
StorageError,
|
|
9
|
+
StorageReadError,
|
|
10
|
+
StorageWriteError,
|
|
11
|
+
StorageDeleteError,
|
|
12
|
+
StorageSerializationError,
|
|
13
|
+
StorageDeserializationError,
|
|
14
|
+
} from '../../domain/errors/StorageError';
|
|
15
|
+
|
|
16
|
+
describe('StorageError Classes', () => {
|
|
17
|
+
describe('StorageError', () => {
|
|
18
|
+
it('should create base storage error', () => {
|
|
19
|
+
const key = 'test-key';
|
|
20
|
+
const cause = new Error('Original error');
|
|
21
|
+
const error = new StorageError(key, cause);
|
|
22
|
+
|
|
23
|
+
expect(error.key).toBe(key);
|
|
24
|
+
expect(error.cause).toBe(cause);
|
|
25
|
+
expect(error.name).toBe('StorageError');
|
|
26
|
+
expect(error.message).toContain(key);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should serialize error details', () => {
|
|
30
|
+
const key = 'test-key';
|
|
31
|
+
const cause = new Error('Original error');
|
|
32
|
+
const error = new StorageError(key, cause);
|
|
33
|
+
|
|
34
|
+
const serialized = JSON.stringify(error);
|
|
35
|
+
const parsed = JSON.parse(serialized);
|
|
36
|
+
|
|
37
|
+
expect(parsed.key).toBe(key);
|
|
38
|
+
expect(parsed.message).toContain(key);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('StorageReadError', () => {
|
|
43
|
+
it('should create read error', () => {
|
|
44
|
+
const key = 'test-key';
|
|
45
|
+
const cause = new Error('Read failed');
|
|
46
|
+
const error = new StorageReadError(key, cause);
|
|
47
|
+
|
|
48
|
+
expect(error.key).toBe(key);
|
|
49
|
+
expect(error.cause).toBe(cause);
|
|
50
|
+
expect(error.name).toBe('StorageReadError');
|
|
51
|
+
expect(error.message).toContain('read');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('StorageWriteError', () => {
|
|
56
|
+
it('should create write error', () => {
|
|
57
|
+
const key = 'test-key';
|
|
58
|
+
const cause = new Error('Write failed');
|
|
59
|
+
const error = new StorageWriteError(key, cause);
|
|
60
|
+
|
|
61
|
+
expect(error.key).toBe(key);
|
|
62
|
+
expect(error.cause).toBe(cause);
|
|
63
|
+
expect(error.name).toBe('StorageWriteError');
|
|
64
|
+
expect(error.message).toContain('write');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('StorageDeleteError', () => {
|
|
69
|
+
it('should create delete error', () => {
|
|
70
|
+
const key = 'test-key';
|
|
71
|
+
const cause = new Error('Delete failed');
|
|
72
|
+
const error = new StorageDeleteError(key, cause);
|
|
73
|
+
|
|
74
|
+
expect(error.key).toBe(key);
|
|
75
|
+
expect(error.cause).toBe(cause);
|
|
76
|
+
expect(error.name).toBe('StorageDeleteError');
|
|
77
|
+
expect(error.message).toContain('delete');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('StorageSerializationError', () => {
|
|
82
|
+
it('should create serialization error', () => {
|
|
83
|
+
const key = 'test-key';
|
|
84
|
+
const cause = new Error('Serialization failed');
|
|
85
|
+
const error = new StorageSerializationError(key, cause);
|
|
86
|
+
|
|
87
|
+
expect(error.key).toBe(key);
|
|
88
|
+
expect(error.cause).toBe(cause);
|
|
89
|
+
expect(error.name).toBe('StorageSerializationError');
|
|
90
|
+
expect(error.message).toContain('serialize');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('StorageDeserializationError', () => {
|
|
95
|
+
it('should create deserialization error', () => {
|
|
96
|
+
const key = 'test-key';
|
|
97
|
+
const cause = new Error('Deserialization failed');
|
|
98
|
+
const error = new StorageDeserializationError(key, cause);
|
|
99
|
+
|
|
100
|
+
expect(error.key).toBe(key);
|
|
101
|
+
expect(error.cause).toBe(cause);
|
|
102
|
+
expect(error.name).toBe('StorageDeserializationError');
|
|
103
|
+
expect(error.message).toContain('deserialize');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Error Inheritance', () => {
|
|
108
|
+
it('should maintain error chain', () => {
|
|
109
|
+
const key = 'test-key';
|
|
110
|
+
const cause = new Error('Original error');
|
|
111
|
+
const error = new StorageReadError(key, cause);
|
|
112
|
+
|
|
113
|
+
expect(error instanceof Error).toBe(true);
|
|
114
|
+
expect(error instanceof StorageError).toBe(true);
|
|
115
|
+
expect(error instanceof StorageReadError).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should preserve stack trace', () => {
|
|
119
|
+
const key = 'test-key';
|
|
120
|
+
const cause = new Error('Original error');
|
|
121
|
+
const error = new StorageReadError(key, cause);
|
|
122
|
+
|
|
123
|
+
expect(error.stack).toBeDefined();
|
|
124
|
+
expect(error.stack).toContain('StorageReadError');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|