@umituz/react-native-design-system 4.27.0 → 4.27.1
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 +1 -1
- package/src/device/detection/deviceDetection.ts +3 -1
- package/src/device/presentation/hooks/useAnonymousUser.ts +8 -13
- package/src/molecules/calendar/infrastructure/storage/CalendarStore.ts +14 -1
- package/src/molecules/navigation/hooks/useAppNavigation.ts +8 -3
- package/src/molecules/navigation/index.ts +1 -1
- package/src/onboarding/presentation/components/BackgroundImageCollage.tsx +3 -3
- package/src/onboarding/presentation/components/OnboardingFooter.tsx +1 -1
- package/src/storage/cache/domain/CacheManager.ts +5 -3
- package/src/storage/cache/presentation/useCachedValue.ts +6 -1
- package/src/storage/presentation/hooks/usePersistentCache.ts +17 -2
- package/src/storage/presentation/hooks/useStorageState.ts +13 -2
- package/src/tanstack/infrastructure/monitoring/DevMonitor.ts +0 -2
- package/src/theme/infrastructure/providers/DesignSystemProvider.tsx +2 -1
- package/src/theme/infrastructure/stores/themeStore.ts +22 -2
- package/src/timezone/infrastructure/utils/SimpleCache.ts +10 -4
- package/src/typography/presentation/utils/textColorUtils.ts +4 -1
- package/src/typography/presentation/utils/textStyleUtils.ts +2 -1
- package/src/utils/async/retryWithBackoff.ts +7 -5
- package/src/utils/constants/TimeConstants.ts +34 -0
- package/src/utils/errors/DesignSystemError.ts +3 -2
- package/src/utils/logger.ts +10 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "4.27.
|
|
3
|
+
"version": "4.27.1",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities - TanStack persistence and expo-image-manipulator now lazy loaded",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -46,7 +46,9 @@ export const isTablet = (): boolean => {
|
|
|
46
46
|
return Device.deviceType === Device.DeviceType.TABLET;
|
|
47
47
|
}
|
|
48
48
|
// Fallback: Platform.isPad (iOS) or screen width >= 600dp (Android)
|
|
49
|
-
|
|
49
|
+
// Platform.isPad is not in React Native types but exists on iOS
|
|
50
|
+
const platformWithIsPad = Platform as { isPad?: boolean };
|
|
51
|
+
if (Platform.OS === 'ios' && platformWithIsPad.isPad) return true;
|
|
50
52
|
const { width, height } = getScreenDimensions();
|
|
51
53
|
return Math.min(width, height) >= 600;
|
|
52
54
|
};
|
|
@@ -46,7 +46,6 @@ export const useAnonymousUser = (
|
|
|
46
46
|
): UseAnonymousUserResult => {
|
|
47
47
|
const {
|
|
48
48
|
anonymousDisplayName = 'Anonymous',
|
|
49
|
-
fallbackUserId = 'anonymous_fallback',
|
|
50
49
|
} = options || {};
|
|
51
50
|
|
|
52
51
|
const { data: anonymousUser, isLoading, error, execute } = useAsyncOperation<AnonymousUser, string>(
|
|
@@ -56,9 +55,14 @@ export const useAnonymousUser = (
|
|
|
56
55
|
DeviceService.getUserFriendlyId(),
|
|
57
56
|
]);
|
|
58
57
|
|
|
58
|
+
// No fallback - if we can't get ID, let it error
|
|
59
|
+
if (!userId) {
|
|
60
|
+
throw new Error('Failed to generate device ID');
|
|
61
|
+
}
|
|
62
|
+
|
|
59
63
|
return {
|
|
60
|
-
userId
|
|
61
|
-
deviceName: deviceName
|
|
64
|
+
userId,
|
|
65
|
+
deviceName: deviceName ?? 'Device',
|
|
62
66
|
displayName: anonymousDisplayName,
|
|
63
67
|
isAnonymous: true,
|
|
64
68
|
};
|
|
@@ -66,16 +70,7 @@ export const useAnonymousUser = (
|
|
|
66
70
|
{
|
|
67
71
|
immediate: true,
|
|
68
72
|
initialData: null,
|
|
69
|
-
errorHandler: () =>
|
|
70
|
-
onError: () => {
|
|
71
|
-
// Fallback on error - set default anonymous user
|
|
72
|
-
return {
|
|
73
|
-
userId: fallbackUserId,
|
|
74
|
-
deviceName: 'Unknown Device',
|
|
75
|
-
displayName: anonymousDisplayName,
|
|
76
|
-
isAnonymous: true,
|
|
77
|
-
};
|
|
78
|
-
},
|
|
73
|
+
errorHandler: (err) => `Failed to generate device ID: ${err instanceof Error ? err.message : String(err)}`,
|
|
79
74
|
}
|
|
80
75
|
);
|
|
81
76
|
|
|
@@ -27,7 +27,13 @@ export const useCalendar = () => {
|
|
|
27
27
|
const view = useCalendarView();
|
|
28
28
|
|
|
29
29
|
// Utility functions - memoized to prevent recreating on every render
|
|
30
|
-
const getEventsForDate = useCallback((date: Date) => {
|
|
30
|
+
const getEventsForDate = useCallback((date: Date | null | undefined) => {
|
|
31
|
+
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
|
32
|
+
if (__DEV__) {
|
|
33
|
+
console.warn('[CalendarStore] getEventsForDate called with invalid date:', date);
|
|
34
|
+
}
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
31
37
|
return events.events.filter(event => {
|
|
32
38
|
const eventDate = new Date(event.date);
|
|
33
39
|
return eventDate.toDateString() === date.toDateString();
|
|
@@ -35,6 +41,13 @@ export const useCalendar = () => {
|
|
|
35
41
|
}, [events.events]);
|
|
36
42
|
|
|
37
43
|
const getEventsForMonth = useCallback((year: number, month: number) => {
|
|
44
|
+
if (typeof year !== 'number' || typeof month !== 'number' ||
|
|
45
|
+
isNaN(year) || isNaN(month) || month < 0 || month > 11) {
|
|
46
|
+
if (__DEV__) {
|
|
47
|
+
console.warn('[CalendarStore] getEventsForMonth called with invalid year/month:', { year, month });
|
|
48
|
+
}
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
38
51
|
return events.events.filter(event => {
|
|
39
52
|
const eventDate = new Date(event.date);
|
|
40
53
|
return eventDate.getFullYear() === year && eventDate.getMonth() === month;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useNavigation, StackActions } from "@react-navigation/native";
|
|
1
|
+
import { useNavigation, StackActions, CommonActions } from "@react-navigation/native";
|
|
2
2
|
import type { NavigationProp, ParamListBase } from "@react-navigation/native";
|
|
3
3
|
import { useCallback, useMemo } from "react";
|
|
4
4
|
|
|
@@ -31,8 +31,13 @@ export function useAppNavigation(): AppNavigationResult {
|
|
|
31
31
|
|
|
32
32
|
const navigate = useCallback(
|
|
33
33
|
(screen: string, params?: Record<string, unknown>) => {
|
|
34
|
-
// Dynamic navigation:
|
|
35
|
-
|
|
34
|
+
// Dynamic navigation: use CommonActions for type-safe arbitrary screen navigation
|
|
35
|
+
navigation.dispatch(
|
|
36
|
+
CommonActions.navigate({
|
|
37
|
+
name: screen,
|
|
38
|
+
params,
|
|
39
|
+
})
|
|
40
|
+
);
|
|
36
41
|
},
|
|
37
42
|
[navigation]
|
|
38
43
|
);
|
|
@@ -46,7 +46,7 @@ export type { NavigationCleanup } from "./utils/NavigationCleanup";
|
|
|
46
46
|
export { AppNavigation } from "./utils/AppNavigation";
|
|
47
47
|
|
|
48
48
|
export { TabLabel, type TabLabelProps } from "./components/TabLabel";
|
|
49
|
-
export
|
|
49
|
+
export { NavigationHeader, type NavigationHeaderProps } from "./components/NavigationHeader";
|
|
50
50
|
export { useTabBarStyles, type TabBarConfig } from "./hooks/useTabBarStyles";
|
|
51
51
|
export { useTabConfig, type UseTabConfigProps } from "./hooks/useTabConfig";
|
|
52
52
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React, { useMemo } from "react";
|
|
8
|
-
import { View, Image as RNImage, StyleSheet } from "react-native";
|
|
8
|
+
import { View, Image as RNImage, StyleSheet, type ImageURISource, type ImageStyle } from "react-native";
|
|
9
9
|
import { useSafeAreaInsets } from "../../../safe-area/hooks/useSafeAreaInsets";
|
|
10
10
|
import {
|
|
11
11
|
generateGridLayout,
|
|
@@ -93,8 +93,8 @@ export const BackgroundImageCollage: React.FC<BackgroundImageCollageProps> = ({
|
|
|
93
93
|
return (
|
|
94
94
|
<RNImage
|
|
95
95
|
key={String(item.source)}
|
|
96
|
-
source={item.source as
|
|
97
|
-
style={item.style as
|
|
96
|
+
source={item.source as ImageURISource | number}
|
|
97
|
+
style={item.style as ImageStyle}
|
|
98
98
|
resizeMode="cover"
|
|
99
99
|
/>
|
|
100
100
|
);
|
|
@@ -52,7 +52,7 @@ export const OnboardingFooter = React.memo<OnboardingFooterProps>(({
|
|
|
52
52
|
const progressFillStyle = useMemo(
|
|
53
53
|
() => ({
|
|
54
54
|
...styles.progressFill,
|
|
55
|
-
width: `${progressPercent}%` as
|
|
55
|
+
width: `${progressPercent}%` as `${number}%`,
|
|
56
56
|
backgroundColor: colors.progressFillColor,
|
|
57
57
|
}),
|
|
58
58
|
[progressPercent, colors.progressFillColor]
|
|
@@ -20,10 +20,12 @@ export class CacheManager {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
getCache<T>(name: string, config?: CacheConfig): Cache<T> {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
let cache = this.caches.get(name);
|
|
24
|
+
if (!cache) {
|
|
25
|
+
cache = new Cache<T>(config);
|
|
26
|
+
this.caches.set(name, cache);
|
|
25
27
|
}
|
|
26
|
-
return
|
|
28
|
+
return cache;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
deleteCache(name: string): boolean {
|
|
@@ -25,7 +25,12 @@ export function useCachedValue<T>(
|
|
|
25
25
|
return cached;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const fetcherFn = fetcherRef.current;
|
|
29
|
+
if (!fetcherFn) {
|
|
30
|
+
throw new Error('Fetcher function is not defined');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const data = await fetcherFn();
|
|
29
34
|
cache.set(key, data, configRef.current?.ttl);
|
|
30
35
|
return data;
|
|
31
36
|
},
|
|
@@ -142,10 +142,25 @@ export function usePersistentCache<T>(
|
|
|
142
142
|
|
|
143
143
|
const setData = useCallback(
|
|
144
144
|
async (value: T) => {
|
|
145
|
-
|
|
145
|
+
// Optimistic update
|
|
146
|
+
const previousData = state.data;
|
|
146
147
|
stableActionsRef.current.setData(value);
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
await cacheOps.saveToStorage(key, value, { ttl, version, enabled });
|
|
151
|
+
} catch (error) {
|
|
152
|
+
// Rollback on error
|
|
153
|
+
if (previousData !== null) {
|
|
154
|
+
stableActionsRef.current.setData(previousData);
|
|
155
|
+
} else {
|
|
156
|
+
stableActionsRef.current.clearData();
|
|
157
|
+
}
|
|
158
|
+
if (__DEV__) {
|
|
159
|
+
console.warn('[usePersistentCache] Failed to save to storage, rolling back:', error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
147
162
|
},
|
|
148
|
-
[key, ttl, version, enabled, cacheOps],
|
|
163
|
+
[key, ttl, version, enabled, cacheOps, state.data],
|
|
149
164
|
);
|
|
150
165
|
|
|
151
166
|
const clearData = useCallback(async () => {
|
|
@@ -57,10 +57,21 @@ export const useStorageState = <T>(
|
|
|
57
57
|
// Update state and persist to storage
|
|
58
58
|
const updateState = useCallback(
|
|
59
59
|
async (value: T) => {
|
|
60
|
+
// Optimistic update
|
|
61
|
+
const previousValue = state;
|
|
60
62
|
setState(value);
|
|
61
|
-
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
await storageRepository.setItem(keyString, value);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// Rollback on error
|
|
68
|
+
setState(previousValue);
|
|
69
|
+
if (__DEV__) {
|
|
70
|
+
console.warn('[useStorageState] Failed to persist state, rolling back:', error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
62
73
|
},
|
|
63
|
-
[keyString]
|
|
74
|
+
[keyString, state]
|
|
64
75
|
);
|
|
65
76
|
|
|
66
77
|
return [state, updateState, isLoading];
|
|
@@ -9,6 +9,7 @@ import type { CustomThemeColors } from '../../core/CustomColors';
|
|
|
9
9
|
import type { SplashScreenProps } from '../../../molecules/splash/types';
|
|
10
10
|
import { useIconStore } from '../../../atoms/icon/iconStore';
|
|
11
11
|
import type { IconRenderer, IconNames } from '../../../atoms/icon/iconStore';
|
|
12
|
+
import { FIVE_SECONDS_MS } from '../../../utils/constants/TimeConstants';
|
|
12
13
|
|
|
13
14
|
// Lazy load SplashScreen to avoid circular dependency
|
|
14
15
|
const SplashScreen = lazy(() => import('../../../molecules/splash').then(m => ({ default: m.SplashScreen })));
|
|
@@ -82,7 +83,7 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
|
|
|
82
83
|
if (!prev) onError?.(new Error('DesignSystemProvider initialization timed out'));
|
|
83
84
|
return true;
|
|
84
85
|
});
|
|
85
|
-
},
|
|
86
|
+
}, FIVE_SECONDS_MS);
|
|
86
87
|
|
|
87
88
|
initialize()
|
|
88
89
|
.then(() => {
|
|
@@ -12,6 +12,26 @@ import { useDesignSystemTheme } from '../globalThemeStore';
|
|
|
12
12
|
import type { ThemeMode } from '../../core/ColorPalette';
|
|
13
13
|
import type { CustomThemeColors } from '../../core/CustomColors';
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Shallow equality check for CustomThemeColors
|
|
17
|
+
* Compares all defined properties without deep object traversal
|
|
18
|
+
*/
|
|
19
|
+
function areCustomColorsEqual(a?: CustomThemeColors, b?: CustomThemeColors): boolean {
|
|
20
|
+
if (a === b) return true;
|
|
21
|
+
if (!a || !b) return false;
|
|
22
|
+
|
|
23
|
+
const keysA = Object.keys(a) as (keyof CustomThemeColors)[];
|
|
24
|
+
const keysB = Object.keys(b) as (keyof CustomThemeColors)[];
|
|
25
|
+
|
|
26
|
+
if (keysA.length !== keysB.length) return false;
|
|
27
|
+
|
|
28
|
+
for (const key of keysA) {
|
|
29
|
+
if (a[key] !== b[key]) return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
15
35
|
interface ThemeState {
|
|
16
36
|
theme: Theme;
|
|
17
37
|
themeMode: ThemeMode;
|
|
@@ -118,8 +138,8 @@ export const useTheme = createStore<ThemeState, ThemeActions>({
|
|
|
118
138
|
const { _updateInProgress, customColors: currentColors } = get();
|
|
119
139
|
if (_updateInProgress) return;
|
|
120
140
|
|
|
121
|
-
//
|
|
122
|
-
if (
|
|
141
|
+
// Shallow comparison to avoid redundant updates from new object references
|
|
142
|
+
if (areCustomColorsEqual(colors, currentColors)) return;
|
|
123
143
|
|
|
124
144
|
const updateId = Date.now();
|
|
125
145
|
set({ _updateInProgress: true, _lastUpdateId: updateId, customColors: colors });
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* No external dependencies - pure TypeScript implementation
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { ONE_MINUTE_MS } from '../../../utils/constants/TimeConstants';
|
|
9
|
+
|
|
8
10
|
interface CacheEntry<T> {
|
|
9
11
|
value: T;
|
|
10
12
|
expires: number;
|
|
@@ -17,7 +19,7 @@ export class SimpleCache<T> {
|
|
|
17
19
|
private destroyed = false;
|
|
18
20
|
private cleanupScheduleLock = false;
|
|
19
21
|
|
|
20
|
-
constructor(defaultTTL: number =
|
|
22
|
+
constructor(defaultTTL: number = ONE_MINUTE_MS) {
|
|
21
23
|
this.defaultTTL = defaultTTL;
|
|
22
24
|
this.scheduleCleanup();
|
|
23
25
|
}
|
|
@@ -91,9 +93,13 @@ export class SimpleCache<T> {
|
|
|
91
93
|
|
|
92
94
|
if (!this.destroyed) {
|
|
93
95
|
this.cleanupTimeout = setTimeout(() => {
|
|
94
|
-
this.
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
if (!this.destroyed) {
|
|
97
|
+
this.cleanupScheduleLock = false;
|
|
98
|
+
this.scheduleCleanup();
|
|
99
|
+
}
|
|
100
|
+
}, ONE_MINUTE_MS);
|
|
101
|
+
} else {
|
|
102
|
+
this.cleanupScheduleLock = false;
|
|
97
103
|
}
|
|
98
104
|
} catch (error) {
|
|
99
105
|
this.cleanupScheduleLock = false;
|
|
@@ -53,7 +53,10 @@ export function getTextColor(
|
|
|
53
53
|
|
|
54
54
|
const cacheKey = `${color}_${Object.keys(tokens.colors).length}_${tokens.colors.textPrimary}`;
|
|
55
55
|
|
|
56
|
-
if (colorCache.has(cacheKey))
|
|
56
|
+
if (colorCache.has(cacheKey)) {
|
|
57
|
+
const cached = colorCache.get(cacheKey);
|
|
58
|
+
if (cached) return cached;
|
|
59
|
+
}
|
|
57
60
|
|
|
58
61
|
const colorKey = COLOR_MAP[color as ColorVariant] ?? 'textPrimary';
|
|
59
62
|
const resolvedColor = tokens.colors[colorKey];
|
|
@@ -112,7 +112,8 @@ export function getTextStyle(
|
|
|
112
112
|
|
|
113
113
|
// Check cache first
|
|
114
114
|
if (typographyCache.has(cacheKey)) {
|
|
115
|
-
|
|
115
|
+
const cached = typographyCache.get(cacheKey);
|
|
116
|
+
if (cached) return cached;
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
// Resolve style and cache it
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* Useful for network requests, file operations, etc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { DEFAULT_LONG_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, ONE_MINUTE_MS, ONE_SECOND_MS, TEN_SECONDS_MS } from '../constants/TimeConstants';
|
|
9
|
+
|
|
8
10
|
export interface RetryOptions {
|
|
9
11
|
/**
|
|
10
12
|
* Maximum number of retry attempts
|
|
@@ -59,14 +61,14 @@ export async function retryWithBackoff<T>(
|
|
|
59
61
|
): Promise<T> {
|
|
60
62
|
const {
|
|
61
63
|
maxRetries = 3,
|
|
62
|
-
baseDelay =
|
|
63
|
-
maxDelay =
|
|
64
|
+
baseDelay = ONE_SECOND_MS,
|
|
65
|
+
maxDelay = TEN_SECONDS_MS,
|
|
64
66
|
backoffMultiplier = 2,
|
|
65
67
|
shouldRetry = () => true,
|
|
66
68
|
onRetry,
|
|
67
69
|
} = options;
|
|
68
70
|
|
|
69
|
-
let lastError: Error;
|
|
71
|
+
let lastError: Error | undefined;
|
|
70
72
|
|
|
71
73
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
72
74
|
try {
|
|
@@ -106,7 +108,7 @@ export async function retryWithBackoff<T>(
|
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
// This should never be reached, but TypeScript needs it
|
|
109
|
-
throw lastError
|
|
111
|
+
throw lastError ?? new Error('Retry operation failed with unknown error');
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
/**
|
|
@@ -125,7 +127,7 @@ export async function retryWithTimeout<T>(
|
|
|
125
127
|
fn: () => Promise<T>,
|
|
126
128
|
options: RetryOptions & { timeout?: number } = {}
|
|
127
129
|
): Promise<T> {
|
|
128
|
-
const { timeout =
|
|
130
|
+
const { timeout = DEFAULT_LONG_TIMEOUT_MS, ...retryOptions } = options;
|
|
129
131
|
|
|
130
132
|
return retryWithBackoff(
|
|
131
133
|
() => withTimeout(fn(), timeout),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time Constants
|
|
3
|
+
*
|
|
4
|
+
* Centralized time-related constants to replace magic numbers
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const MILLISECONDS_PER_SECOND = 1000;
|
|
8
|
+
export const MILLISECONDS_PER_MINUTE = 60 * 1000; // 60000
|
|
9
|
+
export const MILLISECONDS_PER_HOUR = 60 * 60 * 1000; // 3600000
|
|
10
|
+
export const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; // 86400000
|
|
11
|
+
|
|
12
|
+
export const SECONDS_PER_MINUTE = 60;
|
|
13
|
+
export const SECONDS_PER_HOUR = 60 * 60;
|
|
14
|
+
export const SECONDS_PER_DAY = 24 * 60 * 60;
|
|
15
|
+
|
|
16
|
+
export const MINUTES_PER_HOUR = 60;
|
|
17
|
+
export const MINUTES_PER_DAY = 24 * 60;
|
|
18
|
+
|
|
19
|
+
// Common time intervals
|
|
20
|
+
export const ONE_SECOND_MS = MILLISECONDS_PER_SECOND;
|
|
21
|
+
export const FIVE_SECONDS_MS = 5 * MILLISECONDS_PER_SECOND;
|
|
22
|
+
export const TEN_SECONDS_MS = 10 * MILLISECONDS_PER_SECOND;
|
|
23
|
+
export const THIRTY_SECONDS_MS = 30 * MILLISECONDS_PER_SECOND;
|
|
24
|
+
export const ONE_MINUTE_MS = MILLISECONDS_PER_MINUTE;
|
|
25
|
+
export const FIVE_MINUTES_MS = 5 * MILLISECONDS_PER_MINUTE;
|
|
26
|
+
export const TEN_MINUTES_MS = 10 * MILLISECONDS_PER_MINUTE;
|
|
27
|
+
export const THIRTY_MINUTES_MS = 30 * MILLISECONDS_PER_MINUTE;
|
|
28
|
+
export const ONE_HOUR_MS = MILLISECONDS_PER_HOUR;
|
|
29
|
+
export const ONE_DAY_MS = MILLISECONDS_PER_DAY;
|
|
30
|
+
|
|
31
|
+
// Default timeouts
|
|
32
|
+
export const DEFAULT_TIMEOUT_MS = FIVE_SECONDS_MS;
|
|
33
|
+
export const DEFAULT_LONG_TIMEOUT_MS = THIRTY_SECONDS_MS;
|
|
34
|
+
export const DEFAULT_CACHE_TTL_MS = ONE_MINUTE_MS;
|
|
@@ -60,8 +60,9 @@ export class DesignSystemError extends Error {
|
|
|
60
60
|
this.retryable = metadata?.retryable ?? false;
|
|
61
61
|
|
|
62
62
|
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
const ErrorConstructor = Error as { captureStackTrace?: (error: Error, constructor: typeof DesignSystemError) => void };
|
|
64
|
+
if (typeof ErrorConstructor.captureStackTrace === 'function') {
|
|
65
|
+
ErrorConstructor.captureStackTrace(this, DesignSystemError);
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
|
package/src/utils/logger.ts
CHANGED
|
@@ -100,8 +100,11 @@ class Logger {
|
|
|
100
100
|
* Use for performance measurements
|
|
101
101
|
*/
|
|
102
102
|
time(label: string): void {
|
|
103
|
-
if (this.isDev
|
|
104
|
-
|
|
103
|
+
if (this.isDev) {
|
|
104
|
+
const consoleWithTime = console as { time?: (label: string) => void };
|
|
105
|
+
if (typeof consoleWithTime.time === 'function') {
|
|
106
|
+
consoleWithTime.time(label);
|
|
107
|
+
}
|
|
105
108
|
}
|
|
106
109
|
}
|
|
107
110
|
|
|
@@ -109,8 +112,11 @@ class Logger {
|
|
|
109
112
|
* End time measurement - only in development
|
|
110
113
|
*/
|
|
111
114
|
timeEnd(label: string): void {
|
|
112
|
-
if (this.isDev
|
|
113
|
-
|
|
115
|
+
if (this.isDev) {
|
|
116
|
+
const consoleWithTimeEnd = console as { timeEnd?: (label: string) => void };
|
|
117
|
+
if (typeof consoleWithTimeEnd.timeEnd === 'function') {
|
|
118
|
+
consoleWithTimeEnd.timeEnd(label);
|
|
119
|
+
}
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
122
|
|