@umituz/react-native-design-system 2.8.10 → 2.8.12

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "2.8.10",
3
+ "version": "2.8.12",
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, and onboarding utilities",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -26,6 +26,7 @@
26
26
  "./storage": "./src/storage/index.ts",
27
27
  "./filesystem": "./src/filesystem/index.ts",
28
28
  "./media": "./src/media/index.ts",
29
+ "./tanstack": "./src/tanstack/index.ts",
29
30
  "./package.json": "./package.json"
30
31
  },
31
32
  "scripts": {
@@ -59,6 +60,9 @@
59
60
  "url": "https://github.com/umituz/react-native-design-system"
60
61
  },
61
62
  "peerDependencies": {
63
+ "@tanstack/query-async-storage-persister": ">=5.0.0",
64
+ "@tanstack/react-query": ">=5.0.0",
65
+ "@tanstack/react-query-persist-client": ">=5.0.0",
62
66
  "@expo/vector-icons": ">=15.0.0",
63
67
  "@react-native-community/datetimepicker": ">=8.0.0",
64
68
  "@react-native-community/slider": ">=4.0.0",
@@ -100,6 +104,9 @@
100
104
  }
101
105
  },
102
106
  "devDependencies": {
107
+ "@tanstack/query-async-storage-persister": "^5.66.7",
108
+ "@tanstack/react-query": "^5.66.7",
109
+ "@tanstack/react-query-persist-client": "^5.66.7",
103
110
  "@eslint/js": "^9.39.2",
104
111
  "@expo/vector-icons": "^15.0.0",
105
112
  "@react-native-async-storage/async-storage": "^2.2.0",
@@ -0,0 +1 @@
1
+ export * from "../tanstack";
package/src/index.ts CHANGED
@@ -120,3 +120,8 @@ export * from './exports/filesystem';
120
120
  // MEDIA EXPORTS
121
121
  // =============================================================================
122
122
  export * from './exports/media';
123
+
124
+ // =============================================================================
125
+ // TANSTACK EXPORTS
126
+ // =============================================================================
127
+ export * from './exports/tanstack';
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Cache Time Constants
3
+ * Domain layer - Time constants for cache management
4
+ *
5
+ * General-purpose time utilities for any React Native app
6
+ */
7
+
8
+ /**
9
+ * Milliseconds constants for time calculations
10
+ */
11
+ export const TIME_MS = {
12
+ SECOND: 1000,
13
+ MINUTE: 60 * 1000,
14
+ HOUR: 60 * 60 * 1000,
15
+ DAY: 24 * 60 * 60 * 1000,
16
+ WEEK: 7 * 24 * 60 * 60 * 1000,
17
+ } as const;
18
+
19
+ /**
20
+ * Default staleTime values for different cache strategies
21
+ * staleTime = how long data is considered fresh
22
+ */
23
+ export const DEFAULT_STALE_TIME = {
24
+ REALTIME: 0, // Always stale (refetch immediately)
25
+ VERY_SHORT: TIME_MS.MINUTE, // 1 minute
26
+ SHORT: 5 * TIME_MS.MINUTE, // 5 minutes
27
+ MEDIUM: 30 * TIME_MS.MINUTE, // 30 minutes
28
+ LONG: 2 * TIME_MS.HOUR, // 2 hours
29
+ VERY_LONG: TIME_MS.DAY, // 24 hours
30
+ PERMANENT: Infinity, // Never stale
31
+ } as const;
32
+
33
+ /**
34
+ * Default gcTime (garbage collection) values
35
+ * gcTime = how long unused data stays in cache before being garbage collected
36
+ */
37
+ export const DEFAULT_GC_TIME = {
38
+ VERY_SHORT: 5 * TIME_MS.MINUTE, // 5 minutes
39
+ SHORT: 30 * TIME_MS.MINUTE, // 30 minutes
40
+ MEDIUM: 2 * TIME_MS.HOUR, // 2 hours
41
+ LONG: TIME_MS.DAY, // 24 hours
42
+ VERY_LONG: 7 * TIME_MS.DAY, // 7 days
43
+ } as const;
44
+
45
+ /**
46
+ * Default retry configuration
47
+ */
48
+ export const DEFAULT_RETRY = {
49
+ NONE: false,
50
+ MINIMAL: 1,
51
+ STANDARD: 3,
52
+ AGGRESSIVE: 5,
53
+ } as const;
54
+
55
+ /**
56
+ * Default refetch intervals
57
+ */
58
+ export const DEFAULT_REFETCH_INTERVAL = {
59
+ REALTIME: 10 * TIME_MS.SECOND, // 10 seconds
60
+ FAST: 30 * TIME_MS.SECOND, // 30 seconds
61
+ MODERATE: TIME_MS.MINUTE, // 1 minute
62
+ SLOW: 5 * TIME_MS.MINUTE, // 5 minutes
63
+ } as const;
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Base Repository
3
+ * Domain layer - Abstract repository for data operations
4
+ *
5
+ * Provides generic CRUD operations with TanStack Query integration.
6
+ * Subclass this for specific entities to get type-safe data operations.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * class UserRepository extends BaseRepository<User, CreateUserVars, UpdateUserVars> {
11
+ * constructor() {
12
+ * super('users');
13
+ * }
14
+ *
15
+ * async fetchAll(): Promise<User[]> {
16
+ * return api.get('/users');
17
+ * }
18
+ *
19
+ * async fetchById(id: string): Promise<User> {
20
+ * return api.get(`/users/${id}`);
21
+ * }
22
+ *
23
+ * async create(data: CreateUserVars): Promise<User> {
24
+ * return api.post('/users', data);
25
+ * }
26
+ *
27
+ * async update(id: string, data: UpdateUserVars): Promise<User> {
28
+ * return api.put(`/users/${id}`, data);
29
+ * }
30
+ *
31
+ * async remove(id: string): Promise<void> {
32
+ * return api.delete(`/users/${id}`);
33
+ * }
34
+ * }
35
+ * ```
36
+ */
37
+
38
+ import type { QueryClient } from '@tanstack/react-query';
39
+ import { getGlobalQueryClient } from '../../infrastructure/config/QueryClientSingleton';
40
+ import type { CacheConfig } from '../types/CacheStrategy';
41
+ import { CacheStrategies } from '../../infrastructure/config/QueryClientConfig';
42
+
43
+ export interface CreateParams<TVariables> {
44
+ variables: TVariables;
45
+ }
46
+
47
+ export interface UpdateParams<TVariables> {
48
+ id: string | number;
49
+ variables: TVariables;
50
+ }
51
+
52
+ export interface ListParams {
53
+ page?: number;
54
+ limit?: number;
55
+ filter?: Record<string, unknown>;
56
+ }
57
+
58
+ export interface RepositoryOptions {
59
+ /**
60
+ * Cache strategy for queries
61
+ * @default CacheStrategies.PUBLIC_DATA
62
+ */
63
+ cacheStrategy?: CacheConfig;
64
+
65
+ /**
66
+ * Stale time override
67
+ */
68
+ staleTime?: number;
69
+
70
+ /**
71
+ * GC time override
72
+ */
73
+ gcTime?: number;
74
+ }
75
+
76
+ /**
77
+ * Base repository for CRUD operations
78
+ *
79
+ * @template TData - Entity type
80
+ * @template TCreateVariables - Variables for create mutation
81
+ * @template TUpdateVariables - Variables for update mutation
82
+ */
83
+ export abstract class BaseRepository<
84
+ TData,
85
+ TCreateVariables = unknown,
86
+ TUpdateVariables = Partial<TData>,
87
+ > {
88
+ protected readonly resource: string;
89
+ protected readonly options: RepositoryOptions;
90
+
91
+ /**
92
+ * Query key factory for this repository
93
+ */
94
+ public readonly keys: {
95
+ all: () => readonly [string];
96
+ lists: () => readonly [string, 'list'];
97
+ list: (params: ListParams) => readonly [string, 'list', ListParams];
98
+ details: () => readonly [string, 'detail'];
99
+ detail: (id: string | number) => readonly [string, 'detail', string | number];
100
+ };
101
+
102
+ constructor(resource: string, options: RepositoryOptions = {}) {
103
+ this.resource = resource;
104
+ this.options = {
105
+ cacheStrategy: options.cacheStrategy ?? CacheStrategies.PUBLIC_DATA,
106
+ ...options,
107
+ };
108
+
109
+ this.keys = {
110
+ all: () => [this.resource] as const,
111
+ lists: () => [this.resource, 'list'] as const,
112
+ list: (params: ListParams) => [this.resource, 'list', params] as const,
113
+ details: () => [this.resource, 'detail'] as const,
114
+ detail: (id: string | number) => [this.resource, 'detail', id] as const,
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Get query client instance
120
+ */
121
+ protected getClient(): QueryClient {
122
+ return getGlobalQueryClient();
123
+ }
124
+
125
+ /**
126
+ * Get cache options for queries
127
+ */
128
+ protected getCacheOptions(): { staleTime: number; gcTime: number } {
129
+ return {
130
+ staleTime: this.options.staleTime ?? (this.options.cacheStrategy?.staleTime ?? CacheStrategies.PUBLIC_DATA.staleTime),
131
+ gcTime: this.options.gcTime ?? (this.options.cacheStrategy?.gcTime ?? CacheStrategies.PUBLIC_DATA.gcTime),
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Fetch all items - to be implemented by subclass
137
+ */
138
+ abstract fetchAll(params?: ListParams): Promise<TData[]>;
139
+
140
+ /**
141
+ * Fetch item by ID - to be implemented by subclass
142
+ */
143
+ abstract fetchById(id: string | number): Promise<TData>;
144
+
145
+ /**
146
+ * Create item - to be implemented by subclass
147
+ */
148
+ abstract create(params: CreateParams<TCreateVariables>): Promise<TData>;
149
+
150
+ /**
151
+ * Update item - to be implemented by subclass
152
+ */
153
+ abstract update(params: UpdateParams<TUpdateVariables>): Promise<TData>;
154
+
155
+ /**
156
+ * Delete item - to be implemented by subclass
157
+ */
158
+ abstract remove(id: string | number): Promise<void>;
159
+
160
+ /**
161
+ * Query all items with caching
162
+ */
163
+ async queryAll(params?: ListParams): Promise<TData[]> {
164
+ const client = this.getClient();
165
+ const queryKey = params ? this.keys.list(params) : this.keys.lists();
166
+ const cacheOptions = this.getCacheOptions();
167
+
168
+ return client.fetchQuery({
169
+ queryKey,
170
+ queryFn: () => this.fetchAll(params),
171
+ ...cacheOptions,
172
+ });
173
+ }
174
+
175
+ /**
176
+ * Query item by ID with caching
177
+ */
178
+ async queryById(id: string | number): Promise<TData | undefined> {
179
+ const client = this.getClient();
180
+ const queryKey = this.keys.detail(id);
181
+ const cacheOptions = this.getCacheOptions();
182
+
183
+ try {
184
+ return client.fetchQuery({
185
+ queryKey,
186
+ queryFn: () => this.fetchById(id),
187
+ ...cacheOptions,
188
+ });
189
+ } catch {
190
+ return undefined;
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Prefetch all items
196
+ */
197
+ async prefetchAll(params?: ListParams): Promise<void> {
198
+ const client = this.getClient();
199
+ const queryKey = params ? this.keys.list(params) : this.keys.lists();
200
+ const cacheOptions = this.getCacheOptions();
201
+
202
+ await client.prefetchQuery({
203
+ queryKey,
204
+ queryFn: () => this.fetchAll(params),
205
+ ...cacheOptions,
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Prefetch item by ID
211
+ */
212
+ async prefetchById(id: string | number): Promise<void> {
213
+ const client = this.getClient();
214
+ const queryKey = this.keys.detail(id);
215
+ const cacheOptions = this.getCacheOptions();
216
+
217
+ await client.prefetchQuery({
218
+ queryKey,
219
+ queryFn: () => this.fetchById(id),
220
+ ...cacheOptions,
221
+ });
222
+ }
223
+
224
+ /**
225
+ * Invalidate all queries for this resource
226
+ */
227
+ invalidateAll(): Promise<void> {
228
+ const client = this.getClient();
229
+ return client.invalidateQueries({
230
+ predicate: (query) => {
231
+ const key = query.queryKey[0] as string;
232
+ return key === this.resource;
233
+ },
234
+ });
235
+ }
236
+
237
+ /**
238
+ * Invalidate list queries
239
+ */
240
+ invalidateLists(): Promise<void> {
241
+ const client = this.getClient();
242
+ return client.invalidateQueries({
243
+ queryKey: this.keys.lists(),
244
+ });
245
+ }
246
+
247
+ /**
248
+ * Invalidate detail query
249
+ */
250
+ invalidateDetail(id: string | number): Promise<void> {
251
+ const client = this.getClient();
252
+ return client.invalidateQueries({
253
+ queryKey: this.keys.detail(id),
254
+ });
255
+ }
256
+
257
+ /**
258
+ * Set query data (optimistic update)
259
+ */
260
+ setData(id: string | number, data: TData): void {
261
+ const client = this.getClient();
262
+ client.setQueryData(this.keys.detail(id), data);
263
+ }
264
+
265
+ /**
266
+ * Get query data from cache
267
+ */
268
+ getData(id: string | number): TData | undefined {
269
+ const client = this.getClient();
270
+ return client.getQueryData<TData>(this.keys.detail(id));
271
+ }
272
+
273
+ /**
274
+ * Remove query data from cache
275
+ */
276
+ clearData(id: string | number): void {
277
+ const client = this.getClient();
278
+ client.setQueryData(this.keys.detail(id), undefined);
279
+ }
280
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Repository Factory
3
+ * Domain layer - Factory for creating repository instances
4
+ *
5
+ * Provides singleton instances of repositories to avoid multiple instances.
6
+ * Repositories are registered once and reused throughout the app.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * // Register repositories
11
+ * RepositoryFactory.register('users', new UserRepository());
12
+ * RepositoryFactory.register('posts', new PostRepository());
13
+ *
14
+ * // Get repository
15
+ * const userRepo = RepositoryFactory.get<UserRepository>('users');
16
+ * const users = await userRepo.queryAll();
17
+ *
18
+ * // Get all registered keys
19
+ * const keys = RepositoryFactory.keys(); // ['users', 'posts']
20
+ *
21
+ * // Invalidate all repositories
22
+ * RepositoryFactory.invalidateAll();
23
+ * ```
24
+ */
25
+
26
+ import type { BaseRepository } from './BaseRepository';
27
+ import { getGlobalQueryClient } from '../../infrastructure/config/QueryClientSingleton';
28
+
29
+ type RepositoryMap = Map<string, BaseRepository<unknown, unknown, unknown>>;
30
+
31
+ class RepositoryFactoryClass {
32
+ private repositories: RepositoryMap = new Map();
33
+
34
+ /**
35
+ * Register a repository instance
36
+ *
37
+ * @param key - Unique identifier for the repository
38
+ * @param repository - Repository instance
39
+ */
40
+ register<TRepository extends BaseRepository<unknown, unknown, unknown>>(
41
+ key: string,
42
+ repository: TRepository,
43
+ ): void {
44
+ if (this.repositories.has(key)) {
45
+ if (__DEV__) {
46
+
47
+ console.warn(
48
+ `[RepositoryFactory] Repository "${key}" is already registered. Overwriting.`,
49
+ );
50
+ }
51
+ }
52
+ this.repositories.set(key, repository);
53
+ }
54
+
55
+ /**
56
+ * Get a registered repository instance
57
+ *
58
+ * @param key - Repository identifier
59
+ * @throws Error if repository not found
60
+ */
61
+ get<TRepository extends BaseRepository<unknown, unknown, unknown>>(
62
+ key: string,
63
+ ): TRepository {
64
+ const repository = this.repositories.get(key);
65
+ if (!repository) {
66
+ throw new Error(
67
+ `[RepositoryFactory] Repository "${key}" not found. Make sure to register it first.`,
68
+ );
69
+ }
70
+ return repository as TRepository;
71
+ }
72
+
73
+ /**
74
+ * Check if repository is registered
75
+ */
76
+ has(key: string): boolean {
77
+ return this.repositories.has(key);
78
+ }
79
+
80
+ /**
81
+ * Unregister a repository
82
+ */
83
+ unregister(key: string): boolean {
84
+ if (__DEV__ && !this.repositories.has(key)) {
85
+
86
+ console.warn(`[RepositoryFactory] Repository "${key}" is not registered.`);
87
+ }
88
+ return this.repositories.delete(key);
89
+ }
90
+
91
+ /**
92
+ * Get all registered repository keys
93
+ */
94
+ keys(): string[] {
95
+ return Array.from(this.repositories.keys());
96
+ }
97
+
98
+ /**
99
+ * Clear all registered repositories
100
+ * Useful for testing or cleanup
101
+ */
102
+ clear(): void {
103
+ this.repositories.clear();
104
+ }
105
+
106
+ /**
107
+ * Invalidate all queries from all registered repositories
108
+ */
109
+ async invalidateAll(): Promise<void> {
110
+ const client = getGlobalQueryClient();
111
+ await client.invalidateQueries();
112
+ }
113
+
114
+ /**
115
+ * Prefetch all data from all registered repositories
116
+ * Useful for app initialization or online event
117
+ */
118
+ async prefetchAll(): Promise<void> {
119
+ const promises = Array.from(this.repositories.values()).map((repo) => {
120
+ try {
121
+ return repo.prefetchAll();
122
+ } catch {
123
+ // Ignore prefetch errors
124
+ return Promise.resolve();
125
+ }
126
+ });
127
+
128
+ await Promise.all(promises);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Global repository factory instance
134
+ */
135
+ export const RepositoryFactory = new RepositoryFactoryClass();
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Cache Strategy Types
3
+ * Domain layer - Cache configuration types
4
+ *
5
+ * General-purpose cache strategies for any React Native app
6
+ */
7
+
8
+ import type { UseQueryOptions } from '@tanstack/react-query';
9
+
10
+ /**
11
+ * Cache configuration for TanStack Query
12
+ */
13
+ export interface CacheConfig {
14
+ /**
15
+ * Time in ms data is considered fresh (no refetch)
16
+ */
17
+ staleTime: number;
18
+
19
+ /**
20
+ * Time in ms inactive data stays in cache before garbage collection
21
+ */
22
+ gcTime: number;
23
+
24
+ /**
25
+ * Whether to refetch when component mounts
26
+ */
27
+ refetchOnMount?: boolean | 'always';
28
+
29
+ /**
30
+ * Whether to refetch when window regains focus
31
+ */
32
+ refetchOnWindowFocus?: boolean | 'always';
33
+
34
+ /**
35
+ * Whether to refetch when network reconnects
36
+ */
37
+ refetchOnReconnect?: boolean | 'always';
38
+
39
+ /**
40
+ * Number of retry attempts on failure
41
+ */
42
+ retry?: boolean | number;
43
+
44
+ /**
45
+ * Interval for automatic background refetching (in ms)
46
+ * Set to false to disable
47
+ */
48
+ refetchInterval?: number | false;
49
+ }
50
+
51
+ /**
52
+ * Cache strategy enum for different data types
53
+ */
54
+ export enum CacheStrategyType {
55
+ /**
56
+ * Real-time data that changes frequently
57
+ * Example: Live chat, stock prices, sports scores
58
+ */
59
+ REALTIME = 'REALTIME',
60
+
61
+ /**
62
+ * User-specific data
63
+ * Example: User profile, settings, preferences
64
+ */
65
+ USER_DATA = 'USER_DATA',
66
+
67
+ /**
68
+ * Master data that rarely changes
69
+ * Example: Countries list, categories, app configuration
70
+ */
71
+ MASTER_DATA = 'MASTER_DATA',
72
+
73
+ /**
74
+ * Public read-heavy data
75
+ * Example: Blog posts, product catalog, news feed
76
+ */
77
+ PUBLIC_DATA = 'PUBLIC_DATA',
78
+
79
+ /**
80
+ * Custom strategy (user-defined)
81
+ */
82
+ CUSTOM = 'CUSTOM',
83
+ }
84
+
85
+ /**
86
+ * Query options type (generic, works with any data)
87
+ */
88
+ export type QueryConfig<TData = unknown, TError = Error> = Partial<
89
+ UseQueryOptions<TData, TError>
90
+ >;
91
+
92
+ /**
93
+ * Mutation options type (generic, works with any data)
94
+ */
95
+ export interface MutationConfig<TData = unknown, TError = Error, TVariables = unknown> {
96
+ /**
97
+ * Function to call on mutation success
98
+ */
99
+ onSuccess?: (data: TData, variables: TVariables) => void | Promise<void>;
100
+
101
+ /**
102
+ * Function to call on mutation error
103
+ */
104
+ onError?: (error: TError, variables: TVariables) => void | Promise<void>;
105
+
106
+ /**
107
+ * Function to call on mutation settled (success or error)
108
+ */
109
+ onSettled?: (data: TData | undefined, error: TError | null, variables: TVariables) => void | Promise<void>;
110
+
111
+ /**
112
+ * Number of retry attempts
113
+ */
114
+ retry?: boolean | number;
115
+ }