@umituz/react-native-design-system 4.23.66 → 4.23.68
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/atoms/icon/AtomicIcon.tsx +41 -112
- package/src/atoms/icon/components/iconRenderer.tsx +118 -0
- package/src/atoms/icon/utils/iconUtils.ts +94 -0
- package/src/exception/presentation/components/ErrorBoundary.tsx +2 -2
- package/src/molecules/alerts/AlertToast.tsx +163 -192
- package/src/molecules/alerts/utils/alertToastHelpers.ts +70 -0
- package/src/services/api/ApiClient.ts +242 -0
- package/src/services/api/index.ts +9 -0
- package/src/services/api/types/ApiTypes.ts +50 -0
- package/src/services/api/utils/requestBuilder.ts +92 -0
- package/src/services/api/utils/responseHandler.ts +130 -0
- package/src/tanstack/domain/repositories/BaseRepository.ts +16 -72
- package/src/tanstack/domain/repositories/helpers/repositoryHelpers.ts +58 -0
- package/src/tanstack/domain/repositories/mixins/repositoryInvalidationMethods.ts +101 -0
- package/src/tanstack/domain/repositories/mixins/repositoryQueryMethods.ts +102 -0
- package/src/tanstack/presentation/hooks/types/prefetchTypes.ts +33 -0
- package/src/tanstack/presentation/hooks/usePrefetch.ts +8 -28
- package/src/tanstack/presentation/hooks/utils/prefetchLogger.ts +27 -0
- package/src/utils/colorMapper.ts +193 -0
- package/src/utils/formatHelper.ts +16 -0
- package/src/utils/formatters/dateFormatter.ts +64 -0
- package/src/utils/formatters/numberFormatter.ts +130 -0
- package/src/utils/formatters/stringFormatter.ts +190 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/styleComposer.ts +94 -0
- package/src/utils/validationHelper.ts +16 -0
- package/src/utils/validators/dataValidators.ts +111 -0
- package/src/utils/validators/numericValidators.ts +106 -0
- package/src/utils/validators/stringValidators.ts +85 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Service Types
|
|
3
|
+
* Type definitions for API communication
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
7
|
+
|
|
8
|
+
export interface ApiRequestConfig {
|
|
9
|
+
url: string;
|
|
10
|
+
method: HttpMethod;
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
params?: Record<string, string | number | boolean | undefined>;
|
|
13
|
+
body?: any;
|
|
14
|
+
timeout?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ApiResponse<T = any> {
|
|
18
|
+
data: T;
|
|
19
|
+
status: number;
|
|
20
|
+
statusText: string;
|
|
21
|
+
headers: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ApiError {
|
|
25
|
+
message: string;
|
|
26
|
+
status?: number;
|
|
27
|
+
code?: string;
|
|
28
|
+
details?: any;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RequestInterceptor {
|
|
32
|
+
(config: ApiRequestConfig): ApiRequestConfig | Promise<ApiRequestConfig>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ResponseInterceptor {
|
|
36
|
+
(response: ApiResponse): ApiResponse | Promise<ApiResponse>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ErrorInterceptor {
|
|
40
|
+
(error: ApiError): ApiError | Promise<ApiError>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ApiClientConfig {
|
|
44
|
+
baseURL: string;
|
|
45
|
+
timeout?: number;
|
|
46
|
+
headers?: Record<string, string>;
|
|
47
|
+
requestInterceptors?: RequestInterceptor[];
|
|
48
|
+
responseInterceptors?: ResponseInterceptor[];
|
|
49
|
+
errorInterceptors?: ErrorInterceptor[];
|
|
50
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request Builder Utility
|
|
3
|
+
* Builds HTTP requests with proper configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ApiRequestConfig, HttpMethod } from '../types/ApiTypes';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Builds query string from params object
|
|
10
|
+
*
|
|
11
|
+
* @param params - Query parameters
|
|
12
|
+
* @returns Query string
|
|
13
|
+
*/
|
|
14
|
+
export function buildQueryString(params: Record<string, string | number | boolean | undefined>): string {
|
|
15
|
+
const searchParams = new URLSearchParams();
|
|
16
|
+
|
|
17
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
18
|
+
if (value !== undefined) {
|
|
19
|
+
searchParams.append(key, String(value));
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const queryString = searchParams.toString();
|
|
24
|
+
return queryString ? `?${queryString}` : '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Builds full URL with query parameters
|
|
29
|
+
*
|
|
30
|
+
* @param baseURL - Base URL
|
|
31
|
+
* @param path - Request path
|
|
32
|
+
* @param params - Query parameters
|
|
33
|
+
* @returns Full URL
|
|
34
|
+
*/
|
|
35
|
+
export function buildURL(
|
|
36
|
+
baseURL: string,
|
|
37
|
+
path: string,
|
|
38
|
+
params?: Record<string, string | number | boolean | undefined>
|
|
39
|
+
): string {
|
|
40
|
+
const cleanPath = path.startsWith('/') ? path : `/${path}`;
|
|
41
|
+
const fullURL = `${baseURL}${cleanPath}`;
|
|
42
|
+
|
|
43
|
+
if (params) {
|
|
44
|
+
return `${fullURL}${buildQueryString(params)}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return fullURL;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Builds request configuration
|
|
52
|
+
*
|
|
53
|
+
* @param config - Request configuration
|
|
54
|
+
* @returns Built fetch options
|
|
55
|
+
*/
|
|
56
|
+
export function buildRequestConfig(config: ApiRequestConfig): RequestInit {
|
|
57
|
+
const options: RequestInit = {
|
|
58
|
+
method: config.method,
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/json',
|
|
61
|
+
...config.headers,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
if (config.body) {
|
|
66
|
+
options.body = JSON.stringify(config.body);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return options;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a request builder function for a specific base URL
|
|
74
|
+
*
|
|
75
|
+
* @param baseURL - Base URL for all requests
|
|
76
|
+
* @returns Request builder function
|
|
77
|
+
*/
|
|
78
|
+
export function createRequestBuilder(baseURL: string) {
|
|
79
|
+
return (
|
|
80
|
+
path: string,
|
|
81
|
+
method: HttpMethod,
|
|
82
|
+
params?: Record<string, string | number | boolean | undefined>,
|
|
83
|
+
body?: any,
|
|
84
|
+
headers?: Record<string, string>
|
|
85
|
+
): ApiRequestConfig => ({
|
|
86
|
+
url: buildURL(baseURL, path, params),
|
|
87
|
+
method,
|
|
88
|
+
params,
|
|
89
|
+
body,
|
|
90
|
+
headers,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response Handler Utility
|
|
3
|
+
* Handles API responses and errors
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ApiResponse, ApiError } from '../types/ApiTypes';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parses fetch response to ApiResponse
|
|
10
|
+
*
|
|
11
|
+
* @param response - Fetch response
|
|
12
|
+
* @returns Parsed API response
|
|
13
|
+
*/
|
|
14
|
+
export async function parseResponse<T>(response: Response): Promise<ApiResponse<T>> {
|
|
15
|
+
const data = await parseResponseBody<T>(response);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
data,
|
|
19
|
+
status: response.status,
|
|
20
|
+
statusText: response.statusText,
|
|
21
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parses response body based on content type
|
|
27
|
+
*
|
|
28
|
+
* @param response - Fetch response
|
|
29
|
+
* @returns Parsed body data
|
|
30
|
+
*/
|
|
31
|
+
async function parseResponseBody<T>(response: Response): Promise<T> {
|
|
32
|
+
const contentType = response.headers.get('Content-Type');
|
|
33
|
+
|
|
34
|
+
if (contentType?.includes('application/json')) {
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
return data as T;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (contentType?.includes('text/')) {
|
|
40
|
+
const text = await response.text();
|
|
41
|
+
return text as unknown as T;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const blob = await response.blob();
|
|
45
|
+
return blob as unknown as T;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Handles HTTP error and converts to ApiError
|
|
50
|
+
*
|
|
51
|
+
* @param response - Fetch response
|
|
52
|
+
* @returns ApiError object
|
|
53
|
+
*/
|
|
54
|
+
export async function handleHttpError(response: Response): Promise<ApiError> {
|
|
55
|
+
let details: any;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
details = await response.json();
|
|
59
|
+
} catch {
|
|
60
|
+
details = await response.text();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
message: details?.message || response.statusText || 'Request failed',
|
|
65
|
+
status: response.status,
|
|
66
|
+
code: details?.code,
|
|
67
|
+
details,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Handles network error
|
|
73
|
+
*
|
|
74
|
+
* @param error - Error object
|
|
75
|
+
* @returns ApiError object
|
|
76
|
+
*/
|
|
77
|
+
export function handleNetworkError(error: unknown): ApiError {
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
return {
|
|
80
|
+
message: error.message || 'Network error',
|
|
81
|
+
details: error,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
message: 'Unknown network error',
|
|
87
|
+
details: error,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Checks if response is successful
|
|
93
|
+
*
|
|
94
|
+
* @param response - Fetch response
|
|
95
|
+
* @returns True if response is OK
|
|
96
|
+
*/
|
|
97
|
+
export function isSuccessfulResponse(response: Response): boolean {
|
|
98
|
+
return response.ok && response.status >= 200 && response.status < 300;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Creates timeout promise
|
|
103
|
+
*
|
|
104
|
+
* @param ms - Timeout in milliseconds
|
|
105
|
+
* @returns Promise that rejects after timeout
|
|
106
|
+
*/
|
|
107
|
+
export function createTimeoutPromise(ms: number): Promise<never> {
|
|
108
|
+
return new Promise((_, reject) => {
|
|
109
|
+
setTimeout(() => reject(new Error(`Request timeout after ${ms}ms`)), ms);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Wraps fetch with timeout
|
|
115
|
+
*
|
|
116
|
+
* @param url - Request URL
|
|
117
|
+
* @param options - Fetch options
|
|
118
|
+
* @param timeout - Timeout in milliseconds
|
|
119
|
+
* @returns Fetch result with timeout
|
|
120
|
+
*/
|
|
121
|
+
export async function fetchWithTimeout(
|
|
122
|
+
url: string,
|
|
123
|
+
options: RequestInit,
|
|
124
|
+
timeout: number
|
|
125
|
+
): Promise<Response> {
|
|
126
|
+
return Promise.race([
|
|
127
|
+
fetch(url, options),
|
|
128
|
+
createTimeoutPromise(timeout),
|
|
129
|
+
]) as Promise<Response>;
|
|
130
|
+
}
|
|
@@ -35,9 +35,8 @@
|
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
import type { QueryClient
|
|
38
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
39
39
|
import { getGlobalQueryClient } from '../config/QueryClientAccessor';
|
|
40
|
-
import { CacheStrategies } from '../../infrastructure/config/QueryClientConfig';
|
|
41
40
|
import { createQueryKeyFactory } from '../utils/QueryKeyFactory';
|
|
42
41
|
import type {
|
|
43
42
|
CreateParams,
|
|
@@ -45,6 +44,9 @@ import type {
|
|
|
45
44
|
ListParams,
|
|
46
45
|
RepositoryOptions,
|
|
47
46
|
} from './RepositoryTypes';
|
|
47
|
+
import { mergeRepositoryOptions, getCacheOptions } from './helpers/repositoryHelpers';
|
|
48
|
+
import * as queryMethods from './mixins/repositoryQueryMethods';
|
|
49
|
+
import * as invalidationMethods from './mixins/repositoryInvalidationMethods';
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
52
|
* Base repository for CRUD operations
|
|
@@ -68,11 +70,7 @@ export abstract class BaseRepository<
|
|
|
68
70
|
|
|
69
71
|
constructor(resource: string, options: RepositoryOptions = {}) {
|
|
70
72
|
this.resource = resource;
|
|
71
|
-
this.options =
|
|
72
|
-
cacheStrategy: options.cacheStrategy ?? CacheStrategies.PUBLIC_DATA,
|
|
73
|
-
...options,
|
|
74
|
-
};
|
|
75
|
-
|
|
73
|
+
this.options = mergeRepositoryOptions(options);
|
|
76
74
|
this.keys = createQueryKeyFactory(this.resource);
|
|
77
75
|
}
|
|
78
76
|
|
|
@@ -87,10 +85,7 @@ export abstract class BaseRepository<
|
|
|
87
85
|
* Get cache options for queries
|
|
88
86
|
*/
|
|
89
87
|
protected getCacheOptions(): { staleTime: number; gcTime: number } {
|
|
90
|
-
return
|
|
91
|
-
staleTime: this.options.staleTime ?? (this.options.cacheStrategy?.staleTime ?? CacheStrategies.PUBLIC_DATA.staleTime),
|
|
92
|
-
gcTime: this.options.gcTime ?? (this.options.cacheStrategy?.gcTime ?? CacheStrategies.PUBLIC_DATA.gcTime),
|
|
93
|
-
};
|
|
88
|
+
return getCacheOptions(this.options);
|
|
94
89
|
}
|
|
95
90
|
|
|
96
91
|
/**
|
|
@@ -122,120 +117,69 @@ export abstract class BaseRepository<
|
|
|
122
117
|
* Query all items with caching
|
|
123
118
|
*/
|
|
124
119
|
async queryAll(params?: ListParams): Promise<TData[]> {
|
|
125
|
-
|
|
126
|
-
const queryKey = params ? this.keys.list(params as Record<string, unknown>) : this.keys.lists();
|
|
127
|
-
const cacheOptions = this.getCacheOptions();
|
|
128
|
-
|
|
129
|
-
return client.fetchQuery({
|
|
130
|
-
queryKey: queryKey as QueryKey,
|
|
131
|
-
queryFn: () => this.fetchAll(params),
|
|
132
|
-
...cacheOptions,
|
|
133
|
-
});
|
|
120
|
+
return queryMethods.queryAll(this, params);
|
|
134
121
|
}
|
|
135
122
|
|
|
136
123
|
/**
|
|
137
124
|
* Query item by ID with caching
|
|
138
125
|
*/
|
|
139
126
|
async queryById(id: string | number): Promise<TData | undefined> {
|
|
140
|
-
|
|
141
|
-
const queryKey = this.keys.detail(id);
|
|
142
|
-
const cacheOptions = this.getCacheOptions();
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
return client.fetchQuery({
|
|
146
|
-
queryKey: queryKey as QueryKey,
|
|
147
|
-
queryFn: () => this.fetchById(id),
|
|
148
|
-
...cacheOptions,
|
|
149
|
-
});
|
|
150
|
-
} catch {
|
|
151
|
-
return undefined;
|
|
152
|
-
}
|
|
127
|
+
return queryMethods.queryById(this, id);
|
|
153
128
|
}
|
|
154
129
|
|
|
155
130
|
/**
|
|
156
131
|
* Prefetch all items
|
|
157
132
|
*/
|
|
158
133
|
async prefetchAll(params?: ListParams): Promise<void> {
|
|
159
|
-
|
|
160
|
-
const queryKey = params ? this.keys.list(params as Record<string, unknown>) : this.keys.lists();
|
|
161
|
-
const cacheOptions = this.getCacheOptions();
|
|
162
|
-
|
|
163
|
-
await client.prefetchQuery({
|
|
164
|
-
queryKey: queryKey as QueryKey,
|
|
165
|
-
queryFn: () => this.fetchAll(params),
|
|
166
|
-
...cacheOptions,
|
|
167
|
-
});
|
|
134
|
+
return queryMethods.prefetchAll(this, params);
|
|
168
135
|
}
|
|
169
136
|
|
|
170
137
|
/**
|
|
171
138
|
* Prefetch item by ID
|
|
172
139
|
*/
|
|
173
140
|
async prefetchById(id: string | number): Promise<void> {
|
|
174
|
-
|
|
175
|
-
const queryKey = this.keys.detail(id);
|
|
176
|
-
const cacheOptions = this.getCacheOptions();
|
|
177
|
-
|
|
178
|
-
await client.prefetchQuery({
|
|
179
|
-
queryKey: queryKey as QueryKey,
|
|
180
|
-
queryFn: () => this.fetchById(id),
|
|
181
|
-
...cacheOptions,
|
|
182
|
-
});
|
|
141
|
+
return queryMethods.prefetchById(this, id);
|
|
183
142
|
}
|
|
184
143
|
|
|
185
144
|
/**
|
|
186
145
|
* Invalidate all queries for this resource
|
|
187
146
|
*/
|
|
188
147
|
invalidateAll(): Promise<void> {
|
|
189
|
-
|
|
190
|
-
return client.invalidateQueries({
|
|
191
|
-
predicate: (query: { queryKey: readonly unknown[] }) => {
|
|
192
|
-
const key = query.queryKey[0] as string;
|
|
193
|
-
return key === this.resource;
|
|
194
|
-
},
|
|
195
|
-
});
|
|
148
|
+
return invalidationMethods.invalidateAll(this);
|
|
196
149
|
}
|
|
197
150
|
|
|
198
151
|
/**
|
|
199
152
|
* Invalidate list queries
|
|
200
153
|
*/
|
|
201
154
|
invalidateLists(): Promise<void> {
|
|
202
|
-
|
|
203
|
-
return client.invalidateQueries({
|
|
204
|
-
queryKey: this.keys.lists(),
|
|
205
|
-
});
|
|
155
|
+
return invalidationMethods.invalidateLists(this);
|
|
206
156
|
}
|
|
207
157
|
|
|
208
158
|
/**
|
|
209
159
|
* Invalidate detail query
|
|
210
160
|
*/
|
|
211
161
|
invalidateDetail(id: string | number): Promise<void> {
|
|
212
|
-
|
|
213
|
-
return client.invalidateQueries({
|
|
214
|
-
queryKey: this.keys.detail(id),
|
|
215
|
-
});
|
|
162
|
+
return invalidationMethods.invalidateDetail(this, id);
|
|
216
163
|
}
|
|
217
164
|
|
|
218
165
|
/**
|
|
219
166
|
* Set query data (optimistic update)
|
|
220
167
|
*/
|
|
221
168
|
setData(id: string | number, data: TData): void {
|
|
222
|
-
|
|
223
|
-
client.setQueryData(this.keys.detail(id), data);
|
|
169
|
+
invalidationMethods.setData(this, id, data);
|
|
224
170
|
}
|
|
225
171
|
|
|
226
172
|
/**
|
|
227
173
|
* Get query data from cache
|
|
228
174
|
*/
|
|
229
175
|
getData(id: string | number): TData | undefined {
|
|
230
|
-
|
|
231
|
-
return client.getQueryData<TData>(this.keys.detail(id));
|
|
176
|
+
return invalidationMethods.getData(this, id);
|
|
232
177
|
}
|
|
233
178
|
|
|
234
179
|
/**
|
|
235
180
|
* Remove query data from cache
|
|
236
181
|
*/
|
|
237
182
|
clearData(id: string | number): void {
|
|
238
|
-
|
|
239
|
-
client.setQueryData(this.keys.detail(id), undefined);
|
|
183
|
+
invalidationMethods.clearData(this, id);
|
|
240
184
|
}
|
|
241
185
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Helper Functions
|
|
3
|
+
* Common helper functions for repository operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RepositoryOptions } from '../RepositoryTypes';
|
|
7
|
+
import { CacheStrategies } from '../../../infrastructure/config/QueryClientConfig';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Gets cache options for repository queries
|
|
11
|
+
*
|
|
12
|
+
* @param options - Repository options
|
|
13
|
+
* @returns Cache options with staleTime and gcTime
|
|
14
|
+
*/
|
|
15
|
+
export function getCacheOptions(
|
|
16
|
+
options: RepositoryOptions
|
|
17
|
+
): { staleTime: number; gcTime: number } {
|
|
18
|
+
return {
|
|
19
|
+
staleTime:
|
|
20
|
+
options.staleTime ??
|
|
21
|
+
options.cacheStrategy?.staleTime ??
|
|
22
|
+
CacheStrategies.PUBLIC_DATA.staleTime,
|
|
23
|
+
gcTime:
|
|
24
|
+
options.gcTime ??
|
|
25
|
+
options.cacheStrategy?.gcTime ??
|
|
26
|
+
CacheStrategies.PUBLIC_DATA.gcTime,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Merges repository options with defaults
|
|
32
|
+
*
|
|
33
|
+
* @param options - User provided options
|
|
34
|
+
* @returns Merged options
|
|
35
|
+
*/
|
|
36
|
+
export function mergeRepositoryOptions(
|
|
37
|
+
options: RepositoryOptions = {}
|
|
38
|
+
): Required<Pick<RepositoryOptions, 'cacheStrategy'>> & RepositoryOptions {
|
|
39
|
+
return {
|
|
40
|
+
cacheStrategy: options.cacheStrategy ?? CacheStrategies.PUBLIC_DATA,
|
|
41
|
+
...options,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Checks if a query key matches a resource
|
|
47
|
+
*
|
|
48
|
+
* @param queryKey - Query key to check
|
|
49
|
+
* @param resource - Resource name to match
|
|
50
|
+
* @returns True if query key matches resource
|
|
51
|
+
*/
|
|
52
|
+
export function matchesResource(
|
|
53
|
+
queryKey: readonly unknown[],
|
|
54
|
+
resource: string
|
|
55
|
+
): boolean {
|
|
56
|
+
const key = queryKey[0] as string;
|
|
57
|
+
return key === resource;
|
|
58
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Invalidation Methods
|
|
3
|
+
* Cache invalidation methods for repository operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
7
|
+
import type { BaseRepository } from '../BaseRepository';
|
|
8
|
+
import { matchesResource } from '../helpers/repositoryHelpers';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Invalidate all queries for this resource
|
|
12
|
+
*
|
|
13
|
+
* @param repository - Repository instance
|
|
14
|
+
*/
|
|
15
|
+
export function invalidateAll<TData>(
|
|
16
|
+
repository: BaseRepository<TData, unknown, unknown>
|
|
17
|
+
): Promise<void> {
|
|
18
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
19
|
+
const resource = (repository as any).resource;
|
|
20
|
+
|
|
21
|
+
return client.invalidateQueries({
|
|
22
|
+
predicate: (query: { queryKey: readonly unknown[] }) => {
|
|
23
|
+
return matchesResource(query.queryKey, resource);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Invalidate list queries
|
|
30
|
+
*
|
|
31
|
+
* @param repository - Repository instance
|
|
32
|
+
*/
|
|
33
|
+
export function invalidateLists<TData>(
|
|
34
|
+
repository: BaseRepository<TData, unknown, unknown>
|
|
35
|
+
): Promise<void> {
|
|
36
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
37
|
+
return client.invalidateQueries({
|
|
38
|
+
queryKey: repository.keys.lists(),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Invalidate detail query
|
|
44
|
+
*
|
|
45
|
+
* @param repository - Repository instance
|
|
46
|
+
* @param id - Item ID
|
|
47
|
+
*/
|
|
48
|
+
export function invalidateDetail<TData>(
|
|
49
|
+
repository: BaseRepository<TData, unknown, unknown>,
|
|
50
|
+
id: string | number
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
53
|
+
return client.invalidateQueries({
|
|
54
|
+
queryKey: repository.keys.detail(id),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Set query data (optimistic update)
|
|
60
|
+
*
|
|
61
|
+
* @param repository - Repository instance
|
|
62
|
+
* @param id - Item ID
|
|
63
|
+
* @param data - Data to set
|
|
64
|
+
*/
|
|
65
|
+
export function setData<TData>(
|
|
66
|
+
repository: BaseRepository<TData, unknown, unknown>,
|
|
67
|
+
id: string | number,
|
|
68
|
+
data: TData
|
|
69
|
+
): void {
|
|
70
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
71
|
+
client.setQueryData(repository.keys.detail(id), data);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get query data from cache
|
|
76
|
+
*
|
|
77
|
+
* @param repository - Repository instance
|
|
78
|
+
* @param id - Item ID
|
|
79
|
+
* @returns Cached data or undefined
|
|
80
|
+
*/
|
|
81
|
+
export function getData<TData>(
|
|
82
|
+
repository: BaseRepository<TData, unknown, unknown>,
|
|
83
|
+
id: string | number
|
|
84
|
+
): TData | undefined {
|
|
85
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
86
|
+
return client.getQueryData<TData>(repository.keys.detail(id));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Remove query data from cache
|
|
91
|
+
*
|
|
92
|
+
* @param repository - Repository instance
|
|
93
|
+
* @param id - Item ID
|
|
94
|
+
*/
|
|
95
|
+
export function clearData<TData>(
|
|
96
|
+
repository: BaseRepository<TData, unknown, unknown>,
|
|
97
|
+
id: string | number
|
|
98
|
+
): void {
|
|
99
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
100
|
+
client.setQueryData(repository.keys.detail(id), undefined);
|
|
101
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Query Methods
|
|
3
|
+
* Query methods for repository operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { QueryClient, QueryKey } from '@tanstack/react-query';
|
|
7
|
+
import type { ListParams } from '../RepositoryTypes';
|
|
8
|
+
import type { BaseRepository } from '../BaseRepository';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Query all items with caching
|
|
12
|
+
*
|
|
13
|
+
* @param repository - Repository instance
|
|
14
|
+
* @param params - Optional list parameters
|
|
15
|
+
* @returns Promise of data array
|
|
16
|
+
*/
|
|
17
|
+
export async function queryAll<TData>(
|
|
18
|
+
repository: BaseRepository<TData, unknown, unknown>,
|
|
19
|
+
params?: ListParams
|
|
20
|
+
): Promise<TData[]> {
|
|
21
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
22
|
+
const queryKey = params
|
|
23
|
+
? repository.keys.list(params as Record<string, unknown>)
|
|
24
|
+
: repository.keys.lists();
|
|
25
|
+
const cacheOptions = (repository as any).getCacheOptions();
|
|
26
|
+
|
|
27
|
+
return client.fetchQuery({
|
|
28
|
+
queryKey: queryKey as QueryKey,
|
|
29
|
+
queryFn: () => repository.fetchAll(params),
|
|
30
|
+
...cacheOptions,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Query item by ID with caching
|
|
36
|
+
*
|
|
37
|
+
* @param repository - Repository instance
|
|
38
|
+
* @param id - Item ID
|
|
39
|
+
* @returns Promise of data or undefined if not found
|
|
40
|
+
*/
|
|
41
|
+
export async function queryById<TData>(
|
|
42
|
+
repository: BaseRepository<TData, unknown, unknown>,
|
|
43
|
+
id: string | number
|
|
44
|
+
): Promise<TData | undefined> {
|
|
45
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
46
|
+
const queryKey = repository.keys.detail(id);
|
|
47
|
+
const cacheOptions = (repository as any).getCacheOptions();
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return client.fetchQuery({
|
|
51
|
+
queryKey: queryKey as QueryKey,
|
|
52
|
+
queryFn: () => repository.fetchById(id),
|
|
53
|
+
...cacheOptions,
|
|
54
|
+
});
|
|
55
|
+
} catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Prefetch all items
|
|
62
|
+
*
|
|
63
|
+
* @param repository - Repository instance
|
|
64
|
+
* @param params - Optional list parameters
|
|
65
|
+
*/
|
|
66
|
+
export async function prefetchAll<TData>(
|
|
67
|
+
repository: BaseRepository<TData, unknown, unknown>,
|
|
68
|
+
params?: ListParams
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
71
|
+
const queryKey = params
|
|
72
|
+
? repository.keys.list(params as Record<string, unknown>)
|
|
73
|
+
: repository.keys.lists();
|
|
74
|
+
const cacheOptions = (repository as any).getCacheOptions();
|
|
75
|
+
|
|
76
|
+
await client.prefetchQuery({
|
|
77
|
+
queryKey: queryKey as QueryKey,
|
|
78
|
+
queryFn: () => repository.fetchAll(params),
|
|
79
|
+
...cacheOptions,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Prefetch item by ID
|
|
85
|
+
*
|
|
86
|
+
* @param repository - Repository instance
|
|
87
|
+
* @param id - Item ID
|
|
88
|
+
*/
|
|
89
|
+
export async function prefetchById<TData>(
|
|
90
|
+
repository: BaseRepository<TData, unknown, unknown>,
|
|
91
|
+
id: string | number
|
|
92
|
+
): Promise<void> {
|
|
93
|
+
const client = (repository as any).getClient() as QueryClient;
|
|
94
|
+
const queryKey = repository.keys.detail(id);
|
|
95
|
+
const cacheOptions = (repository as any).getCacheOptions();
|
|
96
|
+
|
|
97
|
+
await client.prefetchQuery({
|
|
98
|
+
queryKey: queryKey as QueryKey,
|
|
99
|
+
queryFn: () => repository.fetchById(id),
|
|
100
|
+
...cacheOptions,
|
|
101
|
+
});
|
|
102
|
+
}
|