@savers_app/react-native-sdk 1.1.0

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.
Files changed (197) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +508 -0
  3. package/lib/module/@types/@react-native-async-storage__async-storage.d.js +2 -0
  4. package/lib/module/@types/@react-native-async-storage__async-storage.d.js.map +1 -0
  5. package/lib/module/@types/@react-native-community__geolocation.d.js +2 -0
  6. package/lib/module/@types/@react-native-community__geolocation.d.js.map +1 -0
  7. package/lib/module/@types/react-native-aes-gcm-crypto.d.js +2 -0
  8. package/lib/module/@types/react-native-aes-gcm-crypto.d.js.map +1 -0
  9. package/lib/module/@types/react-native-device-info.d.js +2 -0
  10. package/lib/module/@types/react-native-device-info.d.js.map +1 -0
  11. package/lib/module/core/runtime.js +67 -0
  12. package/lib/module/core/runtime.js.map +1 -0
  13. package/lib/module/data/models/apiResponse.js +19 -0
  14. package/lib/module/data/models/apiResponse.js.map +1 -0
  15. package/lib/module/data/models/onboardingModel.js +67 -0
  16. package/lib/module/data/models/onboardingModel.js.map +1 -0
  17. package/lib/module/data/network/apiClient.js +109 -0
  18. package/lib/module/data/network/apiClient.js.map +1 -0
  19. package/lib/module/data/network/apiEndpoints.js +6 -0
  20. package/lib/module/data/network/apiEndpoints.js.map +1 -0
  21. package/lib/module/data/network/apiExceptionHandler.js +16 -0
  22. package/lib/module/data/network/apiExceptionHandler.js.map +1 -0
  23. package/lib/module/data/network/apiRepository.js +70 -0
  24. package/lib/module/data/network/apiRepository.js.map +1 -0
  25. package/lib/module/data/network/apiService.js +66 -0
  26. package/lib/module/data/network/apiService.js.map +1 -0
  27. package/lib/module/data/network/dataTypeUtils.js +77 -0
  28. package/lib/module/data/network/dataTypeUtils.js.map +1 -0
  29. package/lib/module/data/network/interceptors/authInterceptor.js +69 -0
  30. package/lib/module/data/network/interceptors/authInterceptor.js.map +1 -0
  31. package/lib/module/data/network/interceptors/errorInterceptor.js +15 -0
  32. package/lib/module/data/network/interceptors/errorInterceptor.js.map +1 -0
  33. package/lib/module/data/network/interceptors/loggingInterceptor.js +17 -0
  34. package/lib/module/data/network/interceptors/loggingInterceptor.js.map +1 -0
  35. package/lib/module/data/network/interceptors/networkInterceptor.js +22 -0
  36. package/lib/module/data/network/interceptors/networkInterceptor.js.map +1 -0
  37. package/lib/module/data/network/interceptors/retryInterceptor.js +22 -0
  38. package/lib/module/data/network/interceptors/retryInterceptor.js.map +1 -0
  39. package/lib/module/data/storage/asyncStorageManager.js +50 -0
  40. package/lib/module/data/storage/asyncStorageManager.js.map +1 -0
  41. package/lib/module/data/storage/deviceIdManager.js +45 -0
  42. package/lib/module/data/storage/deviceIdManager.js.map +1 -0
  43. package/lib/module/data/storage/keysManager.js +70 -0
  44. package/lib/module/data/storage/keysManager.js.map +1 -0
  45. package/lib/module/data/storage/locationManager.js +30 -0
  46. package/lib/module/data/storage/locationManager.js.map +1 -0
  47. package/lib/module/data/storage/sessionManager.js +12 -0
  48. package/lib/module/data/storage/sessionManager.js.map +1 -0
  49. package/lib/module/data/storage/storageKeys.js +7 -0
  50. package/lib/module/data/storage/storageKeys.js.map +1 -0
  51. package/lib/module/data/storage/storageManager.js +76 -0
  52. package/lib/module/data/storage/storageManager.js.map +1 -0
  53. package/lib/module/index.js +20 -0
  54. package/lib/module/index.js.map +1 -0
  55. package/lib/module/package.json +1 -0
  56. package/lib/module/services/device/location.js +47 -0
  57. package/lib/module/services/device/location.js.map +1 -0
  58. package/lib/module/services/navigation/dialPad.js +29 -0
  59. package/lib/module/services/navigation/dialPad.js.map +1 -0
  60. package/lib/module/services/navigation/goBackNavigation.js +22 -0
  61. package/lib/module/services/navigation/goBackNavigation.js.map +1 -0
  62. package/lib/module/services/navigation/openBrowser.js +8 -0
  63. package/lib/module/services/navigation/openBrowser.js.map +1 -0
  64. package/lib/module/services/navigation/openMap.js +26 -0
  65. package/lib/module/services/navigation/openMap.js.map +1 -0
  66. package/lib/module/services/permissions/permissionManager.js +28 -0
  67. package/lib/module/services/permissions/permissionManager.js.map +1 -0
  68. package/lib/module/services/url/urlGenerator.js +90 -0
  69. package/lib/module/services/url/urlGenerator.js.map +1 -0
  70. package/lib/module/services/webview/messageHandler.js +69 -0
  71. package/lib/module/services/webview/messageHandler.js.map +1 -0
  72. package/lib/module/utils/dependencyManager.js +51 -0
  73. package/lib/module/utils/dependencyManager.js.map +1 -0
  74. package/lib/module/utils/encryption.js +43 -0
  75. package/lib/module/utils/encryption.js.map +1 -0
  76. package/lib/module/utils/errors.js +9 -0
  77. package/lib/module/utils/errors.js.map +1 -0
  78. package/lib/module/utils/logger.js +14 -0
  79. package/lib/module/utils/logger.js.map +1 -0
  80. package/lib/module/utils/platformManager.js +14 -0
  81. package/lib/module/utils/platformManager.js.map +1 -0
  82. package/lib/module/utils/validator.js +26 -0
  83. package/lib/module/utils/validator.js.map +1 -0
  84. package/lib/typescript/package.json +1 -0
  85. package/lib/typescript/src/core/runtime.d.ts +10 -0
  86. package/lib/typescript/src/core/runtime.d.ts.map +1 -0
  87. package/lib/typescript/src/data/models/apiResponse.d.ts +9 -0
  88. package/lib/typescript/src/data/models/apiResponse.d.ts.map +1 -0
  89. package/lib/typescript/src/data/models/onboardingModel.d.ts +24 -0
  90. package/lib/typescript/src/data/models/onboardingModel.d.ts.map +1 -0
  91. package/lib/typescript/src/data/network/apiClient.d.ts +17 -0
  92. package/lib/typescript/src/data/network/apiClient.d.ts.map +1 -0
  93. package/lib/typescript/src/data/network/apiEndpoints.d.ts +4 -0
  94. package/lib/typescript/src/data/network/apiEndpoints.d.ts.map +1 -0
  95. package/lib/typescript/src/data/network/apiExceptionHandler.d.ts +3 -0
  96. package/lib/typescript/src/data/network/apiExceptionHandler.d.ts.map +1 -0
  97. package/lib/typescript/src/data/network/apiRepository.d.ts +19 -0
  98. package/lib/typescript/src/data/network/apiRepository.d.ts.map +1 -0
  99. package/lib/typescript/src/data/network/apiService.d.ts +19 -0
  100. package/lib/typescript/src/data/network/apiService.d.ts.map +1 -0
  101. package/lib/typescript/src/data/network/dataTypeUtils.d.ts +10 -0
  102. package/lib/typescript/src/data/network/dataTypeUtils.d.ts.map +1 -0
  103. package/lib/typescript/src/data/network/interceptors/authInterceptor.d.ts +13 -0
  104. package/lib/typescript/src/data/network/interceptors/authInterceptor.d.ts.map +1 -0
  105. package/lib/typescript/src/data/network/interceptors/errorInterceptor.d.ts +4 -0
  106. package/lib/typescript/src/data/network/interceptors/errorInterceptor.d.ts.map +1 -0
  107. package/lib/typescript/src/data/network/interceptors/loggingInterceptor.d.ts +5 -0
  108. package/lib/typescript/src/data/network/interceptors/loggingInterceptor.d.ts.map +1 -0
  109. package/lib/typescript/src/data/network/interceptors/networkInterceptor.d.ts +4 -0
  110. package/lib/typescript/src/data/network/interceptors/networkInterceptor.d.ts.map +1 -0
  111. package/lib/typescript/src/data/network/interceptors/retryInterceptor.d.ts +7 -0
  112. package/lib/typescript/src/data/network/interceptors/retryInterceptor.d.ts.map +1 -0
  113. package/lib/typescript/src/data/storage/asyncStorageManager.d.ts +13 -0
  114. package/lib/typescript/src/data/storage/asyncStorageManager.d.ts.map +1 -0
  115. package/lib/typescript/src/data/storage/deviceIdManager.d.ts +2 -0
  116. package/lib/typescript/src/data/storage/deviceIdManager.d.ts.map +1 -0
  117. package/lib/typescript/src/data/storage/keysManager.d.ts +29 -0
  118. package/lib/typescript/src/data/storage/keysManager.d.ts.map +1 -0
  119. package/lib/typescript/src/data/storage/locationManager.d.ts +21 -0
  120. package/lib/typescript/src/data/storage/locationManager.d.ts.map +1 -0
  121. package/lib/typescript/src/data/storage/sessionManager.d.ts +3 -0
  122. package/lib/typescript/src/data/storage/sessionManager.d.ts.map +1 -0
  123. package/lib/typescript/src/data/storage/storageKeys.d.ts +5 -0
  124. package/lib/typescript/src/data/storage/storageKeys.d.ts.map +1 -0
  125. package/lib/typescript/src/data/storage/storageManager.d.ts +16 -0
  126. package/lib/typescript/src/data/storage/storageManager.d.ts.map +1 -0
  127. package/lib/typescript/src/index.d.ts +18 -0
  128. package/lib/typescript/src/index.d.ts.map +1 -0
  129. package/lib/typescript/src/services/device/location.d.ts +8 -0
  130. package/lib/typescript/src/services/device/location.d.ts.map +1 -0
  131. package/lib/typescript/src/services/navigation/dialPad.d.ts +3 -0
  132. package/lib/typescript/src/services/navigation/dialPad.d.ts.map +1 -0
  133. package/lib/typescript/src/services/navigation/goBackNavigation.d.ts +4 -0
  134. package/lib/typescript/src/services/navigation/goBackNavigation.d.ts.map +1 -0
  135. package/lib/typescript/src/services/navigation/openBrowser.d.ts +2 -0
  136. package/lib/typescript/src/services/navigation/openBrowser.d.ts.map +1 -0
  137. package/lib/typescript/src/services/navigation/openMap.d.ts +2 -0
  138. package/lib/typescript/src/services/navigation/openMap.d.ts.map +1 -0
  139. package/lib/typescript/src/services/permissions/permissionManager.d.ts +4 -0
  140. package/lib/typescript/src/services/permissions/permissionManager.d.ts.map +1 -0
  141. package/lib/typescript/src/services/url/urlGenerator.d.ts +43 -0
  142. package/lib/typescript/src/services/url/urlGenerator.d.ts.map +1 -0
  143. package/lib/typescript/src/services/webview/messageHandler.d.ts +2 -0
  144. package/lib/typescript/src/services/webview/messageHandler.d.ts.map +1 -0
  145. package/lib/typescript/src/utils/dependencyManager.d.ts +31 -0
  146. package/lib/typescript/src/utils/dependencyManager.d.ts.map +1 -0
  147. package/lib/typescript/src/utils/encryption.d.ts +5 -0
  148. package/lib/typescript/src/utils/encryption.d.ts.map +1 -0
  149. package/lib/typescript/src/utils/errors.d.ts +4 -0
  150. package/lib/typescript/src/utils/errors.d.ts.map +1 -0
  151. package/lib/typescript/src/utils/logger.d.ts +6 -0
  152. package/lib/typescript/src/utils/logger.d.ts.map +1 -0
  153. package/lib/typescript/src/utils/platformManager.d.ts +4 -0
  154. package/lib/typescript/src/utils/platformManager.d.ts.map +1 -0
  155. package/lib/typescript/src/utils/validator.d.ts +4 -0
  156. package/lib/typescript/src/utils/validator.d.ts.map +1 -0
  157. package/package.json +176 -0
  158. package/src/@types/@react-native-async-storage__async-storage.d.ts +7 -0
  159. package/src/@types/@react-native-community__geolocation.d.ts +18 -0
  160. package/src/@types/react-native-aes-gcm-crypto.d.ts +22 -0
  161. package/src/@types/react-native-device-info.d.ts +3 -0
  162. package/src/core/runtime.ts +80 -0
  163. package/src/data/models/apiResponse.ts +28 -0
  164. package/src/data/models/onboardingModel.ts +99 -0
  165. package/src/data/network/apiClient.ts +148 -0
  166. package/src/data/network/apiEndpoints.ts +3 -0
  167. package/src/data/network/apiExceptionHandler.ts +16 -0
  168. package/src/data/network/apiRepository.ts +106 -0
  169. package/src/data/network/apiService.ts +72 -0
  170. package/src/data/network/dataTypeUtils.ts +78 -0
  171. package/src/data/network/interceptors/authInterceptor.ts +77 -0
  172. package/src/data/network/interceptors/errorInterceptor.ts +13 -0
  173. package/src/data/network/interceptors/loggingInterceptor.ts +16 -0
  174. package/src/data/network/interceptors/networkInterceptor.ts +20 -0
  175. package/src/data/network/interceptors/retryInterceptor.ts +24 -0
  176. package/src/data/storage/asyncStorageManager.ts +56 -0
  177. package/src/data/storage/deviceIdManager.ts +46 -0
  178. package/src/data/storage/keysManager.ts +89 -0
  179. package/src/data/storage/locationManager.ts +39 -0
  180. package/src/data/storage/sessionManager.ts +11 -0
  181. package/src/data/storage/storageKeys.ts +4 -0
  182. package/src/data/storage/storageManager.ts +83 -0
  183. package/src/index.tsx +17 -0
  184. package/src/services/device/location.ts +51 -0
  185. package/src/services/navigation/dialPad.ts +39 -0
  186. package/src/services/navigation/goBackNavigation.ts +22 -0
  187. package/src/services/navigation/openBrowser.ts +6 -0
  188. package/src/services/navigation/openMap.ts +39 -0
  189. package/src/services/permissions/permissionManager.ts +33 -0
  190. package/src/services/url/urlGenerator.ts +132 -0
  191. package/src/services/webview/messageHandler.ts +48 -0
  192. package/src/utils/dependencyManager.ts +82 -0
  193. package/src/utils/encryption.ts +58 -0
  194. package/src/utils/errors.ts +6 -0
  195. package/src/utils/logger.ts +11 -0
  196. package/src/utils/platformManager.ts +14 -0
  197. package/src/utils/validator.ts +29 -0
@@ -0,0 +1,80 @@
1
+ // Utilities
2
+ import { logger } from '../utils/logger';
3
+ import { SDKRequirements } from '../utils/dependencyManager';
4
+
5
+ // Data / Storage
6
+ import {
7
+ setApiKey,
8
+ setEncryptionKey,
9
+ setPRefCode,
10
+ setAuthMode,
11
+ } from '../data/storage/keysManager';
12
+ import { getDeviceId } from '../data/storage/deviceIdManager';
13
+
14
+ // Services
15
+ // import { getDeviceLocation } from '../services/device/location';
16
+
17
+ export function initializeSDK(providedKeys: {
18
+ apiKey: string;
19
+ encryptionKey: string;
20
+ pRefCode: string;
21
+ authMode: string;
22
+ }) {
23
+ const keys = providedKeys;
24
+ const missing: string[] = [];
25
+ if (!keys.apiKey) missing.push('apiKey');
26
+ if (!keys.encryptionKey) missing.push('encryptionKey');
27
+ if (!keys.pRefCode) missing.push('pRefCode');
28
+ if (!keys.authMode) missing.push('authMode');
29
+
30
+ if (missing.length) {
31
+ const msg = `[SDK] Missing required keys: ${missing.join(', ')}`;
32
+ logger.warn(msg);
33
+ throw new Error(msg);
34
+ }
35
+ setApiKey(keys.apiKey);
36
+ setEncryptionKey(keys.encryptionKey);
37
+ setPRefCode(keys.pRefCode);
38
+ setAuthMode(keys.authMode);
39
+
40
+ getDeviceId()
41
+ .then((id) => {
42
+ logger.info('[SDK] Device ID availability', { deviceId: Boolean(id) });
43
+ })
44
+ .catch(() => {
45
+ logger.warn('[SDK] Device ID unavailable');
46
+ });
47
+
48
+ // getDeviceLocation()
49
+ // .then((loc) => {
50
+ // logger.info('[SDK] Device location availability', {
51
+ // location: Boolean(loc),
52
+ // });
53
+ // })
54
+ // .catch(() => {
55
+ // logger.warn('[SDK] Device location unavailable');
56
+ // });
57
+
58
+ const { asyncStorage, geolocation, deviceInfo } = SDKRequirements;
59
+ if (asyncStorage && geolocation && deviceInfo) return;
60
+ if (!asyncStorage) {
61
+ logger.warn(
62
+ '[SDK] Missing AsyncStorage. Install @react-native-async-storage/async-storage for session/device features.'
63
+ );
64
+ }
65
+ if (!geolocation) {
66
+ logger.warn(
67
+ '[SDK] Missing Geolocation. Install @react-native-community/geolocation for location features.'
68
+ );
69
+ }
70
+
71
+ if (!deviceInfo) {
72
+ logger.warn(
73
+ '[SDK] Missing DeviceInfo. Install react-native-device-info for device identification.'
74
+ );
75
+ }
76
+ }
77
+
78
+ export const SaversAppSDK = {
79
+ initialized: initializeSDK,
80
+ };
@@ -0,0 +1,28 @@
1
+ export type ApiResponse<T> = {
2
+ data?: T | null;
3
+ status?: boolean;
4
+ message?: string;
5
+ fromCache: boolean;
6
+ };
7
+
8
+ export function apiError(message?: string): ApiResponse<null> {
9
+ return {
10
+ data: null,
11
+ status: false,
12
+ message: message ?? '',
13
+ fromCache: false,
14
+ };
15
+ }
16
+
17
+ export function apiSuccess<T>(
18
+ data: T,
19
+ message?: string,
20
+ fromCache?: boolean
21
+ ): ApiResponse<T> {
22
+ return {
23
+ data,
24
+ status: true,
25
+ message: message ?? '',
26
+ fromCache: !!fromCache,
27
+ };
28
+ }
@@ -0,0 +1,99 @@
1
+ export type OnBoardingImage = {
2
+ id?: number | null;
3
+ url?: string | null;
4
+ };
5
+
6
+ export function onBoardingImageFromJson(
7
+ json: Record<string, any>
8
+ ): OnBoardingImage {
9
+ return {
10
+ id: typeof json['id'] === 'number' ? json['id'] : null,
11
+ url: typeof json['url'] === 'string' ? json['url'] : null,
12
+ };
13
+ }
14
+
15
+ export function onBoardingImageListFromJson(list: any[]): OnBoardingImage[] {
16
+ const out: OnBoardingImage[] = [];
17
+ for (const item of list) {
18
+ if (item && typeof item === 'object' && !Array.isArray(item)) {
19
+ try {
20
+ out.push(onBoardingImageFromJson(item as Record<string, any>));
21
+ } catch {}
22
+ }
23
+ }
24
+ return out;
25
+ }
26
+
27
+ export type OnBoardingThemeData = {
28
+ total?: number | null;
29
+ images?: OnBoardingImage[] | null;
30
+ };
31
+
32
+ export function onBoardingThemeFromJson(json: any): OnBoardingThemeData {
33
+ const map = { ...(json as Record<string, any>) };
34
+ const imagesRaw = Array.isArray(map['images'])
35
+ ? (map['images'] as any[])
36
+ : [];
37
+ const images = onBoardingImageListFromJson(imagesRaw);
38
+ const total =
39
+ typeof map['total'] === 'number'
40
+ ? map['total']
41
+ : typeof map['total'] === 'string'
42
+ ? Number(map['total'])
43
+ : null;
44
+ return {
45
+ total: total ?? null,
46
+ images: images.length > 0 ? images : null,
47
+ };
48
+ }
49
+
50
+ export type OnBoardingData = {
51
+ day?: OnBoardingThemeData | null;
52
+ night?: OnBoardingThemeData | null;
53
+ };
54
+
55
+ export function onBoardingDataFromJson(json: any): OnBoardingData {
56
+ const map = { ...(json as Record<string, any>) };
57
+ const hasDayNight = 'day' in map || 'night' in map;
58
+ if (hasDayNight) {
59
+ const day = map['day'] ? onBoardingThemeFromJson(map['day']) : null;
60
+ const night = map['night'] ? onBoardingThemeFromJson(map['night']) : null;
61
+ return { day, night };
62
+ }
63
+ const legacyImages = Array.isArray(map['images'])
64
+ ? onBoardingImageListFromJson(map['images'] as any[])
65
+ : [];
66
+ const legacyTotal =
67
+ typeof map['total'] === 'number'
68
+ ? map['total']
69
+ : typeof map['total'] === 'string'
70
+ ? Number(map['total'])
71
+ : null;
72
+ const legacyTheme: OnBoardingThemeData = {
73
+ total: legacyTotal ?? null,
74
+ images: legacyImages.length > 0 ? legacyImages : null,
75
+ };
76
+ return { day: legacyTheme, night: null };
77
+ }
78
+
79
+ export const images = (d?: OnBoardingData | null): OnBoardingImage[] | null =>
80
+ d?.day?.images ?? d?.night?.images ?? null;
81
+ export const total = (d?: OnBoardingData | null): number | null =>
82
+ d?.day?.total ?? d?.night?.total ?? null;
83
+ export const hasTotal = (d?: OnBoardingData | null): boolean =>
84
+ total(d) !== null;
85
+ export const hasImages = (d?: OnBoardingData | null): boolean =>
86
+ !!images(d) && (images(d)?.length ?? 0) > 0;
87
+ export const imageCount = (d?: OnBoardingData | null): number =>
88
+ images(d)?.length ?? 0;
89
+ export const isValid = (d?: OnBoardingData | null): boolean =>
90
+ !!(d?.day?.total && d?.day?.images) ||
91
+ !!(d?.night?.total && d?.night?.images);
92
+ export const getImage = (
93
+ d: OnBoardingData | null | undefined,
94
+ index: number
95
+ ): OnBoardingImage | null => {
96
+ const list = images(d);
97
+ if (list && index >= 0 && index < list.length) return list[index] ?? null;
98
+ return null;
99
+ };
@@ -0,0 +1,148 @@
1
+ /* eslint-disable prettier/prettier */
2
+
3
+ // Utilities / Helpers
4
+ import { logger } from '../../utils/logger';
5
+
6
+ // Services / API clients
7
+ import { AuthInterceptor } from './interceptors/authInterceptor';
8
+ import { RetryInterceptor } from './interceptors/retryInterceptor';
9
+ import { ErrorInterceptor } from './interceptors/errorInterceptor';
10
+ import { NetworkInterceptor } from './interceptors/networkInterceptor';
11
+ import { LoggingInterceptor } from './interceptors/loggingInterceptor';
12
+
13
+ export enum Environment {
14
+ production = 'production',
15
+ staging = 'staging',
16
+ }
17
+
18
+ export class ApiClient {
19
+ private static readonly baseUrls: Record<Environment, string> = {
20
+ [Environment.production]: 'https://....',
21
+ [Environment.staging]: 'https://....',
22
+ };
23
+
24
+ private baseUrl: string;
25
+ private interceptors: any[] = [];
26
+
27
+ constructor(environment: Environment = Environment.production) {
28
+ this.baseUrl = ApiClient.baseUrls[environment];
29
+
30
+ this.interceptors = [
31
+ new LoggingInterceptor(),
32
+ new NetworkInterceptor(),
33
+ new AuthInterceptor(),
34
+ new RetryInterceptor(2),
35
+ new ErrorInterceptor(),
36
+ ];
37
+ }
38
+
39
+ async get(endpoint: string, params?: Record<string, any>): Promise<any> {
40
+ return this.request('GET', endpoint, undefined, params);
41
+ }
42
+
43
+ async post(
44
+ endpoint: string,
45
+ data?: any,
46
+ params?: Record<string, any>
47
+ ): Promise<any> {
48
+ return this.request('POST', endpoint, data, params);
49
+ }
50
+
51
+ async put(
52
+ endpoint: string,
53
+ data?: any,
54
+ params?: Record<string, any>
55
+ ): Promise<any> {
56
+ return this.request('PUT', endpoint, data, params);
57
+ }
58
+
59
+ async delete(
60
+ endpoint: string,
61
+ data?: any,
62
+ params?: Record<string, any>
63
+ ): Promise<any> {
64
+ return this.request('DELETE', endpoint, data, params);
65
+ }
66
+
67
+ private async request(
68
+ method: string,
69
+ endpoint: string,
70
+ data?: any,
71
+ params?: Record<string, any>
72
+ ): Promise<any> {
73
+ const url = this.buildUrl(endpoint, params);
74
+ let options: any = {
75
+ method,
76
+ url,
77
+ headers: {
78
+ 'Content-Type': 'application/json',
79
+ },
80
+ body: data,
81
+ params, // kept for interceptors
82
+ };
83
+
84
+ // Apply request interceptors
85
+ for (const interceptor of this.interceptors) {
86
+ if (interceptor.onRequest) {
87
+ options = await interceptor.onRequest(options);
88
+ if (!options) throw new Error('Interceptor blocked request');
89
+ }
90
+ }
91
+
92
+ try {
93
+ const response = await fetch(options.url, {
94
+ method: options.method,
95
+ headers: options.headers,
96
+ body: options.body ? JSON.stringify(options.body) : undefined,
97
+ });
98
+
99
+ let responseData: any = null;
100
+ try {
101
+ responseData = await response.json();
102
+ } catch {
103
+ // ignore JSON parse error
104
+ }
105
+
106
+ // Apply response interceptors
107
+ let responseObj = {
108
+ data: responseData,
109
+ status: response.status,
110
+ statusText: response.statusText,
111
+ };
112
+
113
+ for (const interceptor of this.interceptors) {
114
+ if (interceptor.onResponse) {
115
+ responseObj = await interceptor.onResponse(responseObj);
116
+ }
117
+ }
118
+
119
+ return responseObj;
120
+ } catch (error) {
121
+ // Apply error interceptors
122
+ logger.error('API Request failed', error);
123
+ const handledError = error;
124
+ for (const interceptor of this.interceptors) {
125
+ if (interceptor.onError) {
126
+ // This is complex to mock fully without a real handler
127
+ // interceptor.onError(handledError, handler);
128
+ }
129
+ }
130
+ throw handledError;
131
+ }
132
+ }
133
+
134
+ private buildUrl(endpoint: string, params?: Record<string, any>): string {
135
+ let url = `${this.baseUrl}${endpoint}`;
136
+ if (params) {
137
+ const queryString = Object.keys(params)
138
+ .map(
139
+ (key) => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`
140
+ )
141
+ .join('&');
142
+ if (queryString) {
143
+ url += `?${queryString}`;
144
+ }
145
+ }
146
+ return url;
147
+ }
148
+ }
@@ -0,0 +1,3 @@
1
+ export const ApiEndpoints = {
2
+ onboarding: '/on-boarding/listings',
3
+ } as const;
@@ -0,0 +1,16 @@
1
+ export function getErrorMessage(status?: number, type?: string): string {
2
+ if (type === 'timeout') return 'Connection timeout. Please try again.';
3
+ if (type === 'sendtimeout') return 'Send timeout. Please try again.';
4
+ if (type === 'receivetimeout') return 'Receive timeout. Please try again.';
5
+ if (typeof status === 'number') {
6
+ if (status >= 400 && status < 500)
7
+ return `Request error [${status}]. Please check your input.`;
8
+ if (status >= 500)
9
+ return `Server error [${status}]. Please try again later.`;
10
+ }
11
+ return 'Something went wrong. Please try again.';
12
+ }
13
+
14
+ export function noInternetError(): string {
15
+ return 'No internet connection. Please check your network.';
16
+ }
@@ -0,0 +1,106 @@
1
+ // Services / API clients
2
+ import { ApiClient } from './apiClient';
3
+
4
+ // Types / Models
5
+ import type { ApiResponse } from '../models/apiResponse';
6
+ import { apiError, apiSuccess } from '../models/apiResponse';
7
+
8
+ // Storage
9
+ import {
10
+ get as storageGet,
11
+ save as storageSave,
12
+ } from '../storage/storageManager';
13
+
14
+ export enum CacheStrategy {
15
+ cache = 'cache',
16
+ cacheOnly = 'cacheOnly',
17
+ network = 'network',
18
+ normal = 'normal',
19
+ }
20
+
21
+ export class ApiRepository {
22
+ private readonly apiClient: ApiClient;
23
+
24
+ constructor(apiClient: ApiClient) {
25
+ this.apiClient = apiClient;
26
+ }
27
+
28
+ async *getData<T>(
29
+ endpoint: string,
30
+ opts?: {
31
+ cacheStrategy?: CacheStrategy;
32
+ params?: Record<string, any>;
33
+ storageKeyPrefix?: string;
34
+ }
35
+ ): AsyncGenerator<ApiResponse<T | null>, void, unknown> {
36
+ const cacheStrategy = opts?.cacheStrategy ?? CacheStrategy.normal;
37
+ const cacheKey =
38
+ (opts?.storageKeyPrefix ?? '') +
39
+ endpoint +
40
+ JSON.stringify(opts?.params ?? {});
41
+
42
+ if (cacheStrategy === CacheStrategy.cache) {
43
+ const cached = await storageGet(cacheKey);
44
+ if (cached !== null && cached !== undefined) {
45
+ yield apiSuccess<T>(cached as T, 'Data fetched from cache', true);
46
+ } else {
47
+ const res = await this.fetchFromNetwork<T>(
48
+ endpoint,
49
+ opts?.params,
50
+ cacheKey
51
+ );
52
+ yield res;
53
+ }
54
+ return;
55
+ }
56
+
57
+ if (cacheStrategy === CacheStrategy.cacheOnly) {
58
+ const cached = await storageGet(cacheKey);
59
+ if (cached !== null && cached !== undefined) {
60
+ yield apiSuccess<T>(cached as T, 'Data fetched from cache', true);
61
+ } else {
62
+ yield apiError('No cached data found') as ApiResponse<T | null>;
63
+ }
64
+ return;
65
+ }
66
+
67
+ if (cacheStrategy === CacheStrategy.network) {
68
+ const res = await this.fetchFromNetwork<T>(
69
+ endpoint,
70
+ opts?.params,
71
+ cacheKey
72
+ );
73
+ yield res;
74
+ return;
75
+ }
76
+
77
+ const cached = await storageGet(cacheKey);
78
+ if (cached !== null && cached !== undefined) {
79
+ yield apiSuccess<T>(cached as T, 'Data fetched from cache', true);
80
+ }
81
+ const res = await this.fetchFromNetwork<T>(
82
+ endpoint,
83
+ opts?.params,
84
+ cacheKey
85
+ );
86
+ yield res;
87
+ }
88
+
89
+ private async fetchFromNetwork<T>(
90
+ endpoint: string,
91
+ params?: Record<string, any>,
92
+ cacheKey?: string
93
+ ): Promise<ApiResponse<T | null>> {
94
+ try {
95
+ const response = await this.apiClient.get(endpoint, params);
96
+ const data = response?.data ?? null;
97
+ const message = response?.message ?? response?.statusText ?? 'No message';
98
+ if (data !== null && cacheKey) {
99
+ await storageSave(cacheKey, data);
100
+ }
101
+ return apiSuccess<T>(data, message, false);
102
+ } catch (e) {
103
+ return apiError(`Network request failed: ${e}`) as ApiResponse<T | null>;
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,72 @@
1
+ // Utilities
2
+ import { logger } from '../../utils/logger';
3
+
4
+ // Types / Models
5
+ import type { ApiResponse } from '../models/apiResponse';
6
+ import {
7
+ type OnBoardingData,
8
+ onBoardingDataFromJson,
9
+ } from '../models/onboardingModel';
10
+
11
+ // Storage
12
+ import { clearAll as storageClearAll } from '../storage/storageManager';
13
+
14
+ // API
15
+ import { ApiEndpoints } from './apiEndpoints';
16
+ import { ApiRepository, CacheStrategy } from './apiRepository';
17
+ import { Environment, ApiClient } from './apiClient';
18
+
19
+ export class ApiService {
20
+ private repository: ApiRepository;
21
+
22
+ constructor(environment: Environment = Environment.production) {
23
+ const apiClient = new ApiClient(environment);
24
+ this.repository = new ApiRepository(apiClient);
25
+ logger.info(`ApiService initialized with environment: ${environment}`);
26
+ }
27
+
28
+ /**
29
+ * Get onboarding data using GenericRepository with stream
30
+ */
31
+ async *getOnBoarding(opts?: {
32
+ cacheStrategy?: CacheStrategy;
33
+ }): AsyncGenerator<ApiResponse<OnBoardingData | null>, void, unknown> {
34
+ const generator = this.repository.getData<any>(ApiEndpoints.onboarding, {
35
+ cacheStrategy: opts?.cacheStrategy ?? CacheStrategy.normal,
36
+ });
37
+
38
+ for await (const response of generator) {
39
+ if (response.status === true && response.data) {
40
+ try {
41
+ const parsedData = onBoardingDataFromJson(response.data);
42
+ yield {
43
+ ...response,
44
+ data: parsedData,
45
+ };
46
+ } catch (e) {
47
+ yield {
48
+ status: false,
49
+ message: `Failed to parse onboarding data: ${e}`,
50
+ data: null,
51
+ fromCache: response.fromCache,
52
+ };
53
+ }
54
+ } else {
55
+ yield {
56
+ status: false,
57
+ message: response.message ?? 'Failed to load onboarding data',
58
+ data: null,
59
+ fromCache: response.fromCache,
60
+ };
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Clear all cached data
67
+ */
68
+ async clearCache(): Promise<void> {
69
+ await storageClearAll();
70
+ logger.info('Cache cleared');
71
+ }
72
+ }
@@ -0,0 +1,78 @@
1
+ export function parseToString(value: any): string | null {
2
+ if (value === null || value === undefined) return null;
3
+ if (typeof value === 'string') return value;
4
+ if (typeof value === 'number') return String(value);
5
+ if (typeof value === 'boolean') return String(value);
6
+ return String(value);
7
+ }
8
+
9
+ export function parseToInt(value: any): number | null {
10
+ if (value === null || value === undefined) return null;
11
+ if (typeof value === 'number') return Math.trunc(value);
12
+ if (typeof value === 'string') {
13
+ const n = Number(value);
14
+ if (!Number.isNaN(n)) return Math.trunc(n);
15
+ }
16
+ return null;
17
+ }
18
+
19
+ export function parseToDouble(value: any): number | null {
20
+ if (value === null || value === undefined) return null;
21
+ if (typeof value === 'number') return value;
22
+ if (typeof value === 'string') {
23
+ const n = Number(value);
24
+ if (!Number.isNaN(n)) return n;
25
+ }
26
+ return null;
27
+ }
28
+
29
+ export function parseToBool(value: any): boolean | null {
30
+ if (value === null || value === undefined) return null;
31
+ if (typeof value === 'boolean') return value;
32
+ if (typeof value === 'number') return value === 1;
33
+ if (typeof value === 'string') {
34
+ const v = value.toLowerCase();
35
+ if (v === 'true' || v === '1' || v === 'yes') return true;
36
+ if (v === 'false' || v === '0' || v === 'no') return false;
37
+ }
38
+ return null;
39
+ }
40
+
41
+ export function parseToMap(value: any): Record<string, any> | null {
42
+ if (value === null || value === undefined) return null;
43
+ if (typeof value === 'object' && !Array.isArray(value)) {
44
+ return { ...value };
45
+ }
46
+ return null;
47
+ }
48
+
49
+ export function parseToList(value: any): any[] | null {
50
+ if (Array.isArray(value)) return value;
51
+ return null;
52
+ }
53
+
54
+ export function parseToStringList(value: any): string[] | null {
55
+ if (!Array.isArray(value)) return null;
56
+ const out: string[] = [];
57
+ for (const item of value) {
58
+ const s = parseToString(item);
59
+ if (s !== null) out.push(s);
60
+ }
61
+ return out;
62
+ }
63
+
64
+ export function isNullOrEmpty(value: any): boolean {
65
+ if (value === null || value === undefined) return true;
66
+ if (typeof value === 'string') return value.length === 0;
67
+ if (Array.isArray(value)) return value.length === 0;
68
+ if (typeof value === 'object') return Object.keys(value).length === 0;
69
+ return false;
70
+ }
71
+
72
+ export function safeParseForModel(data: any): any {
73
+ if (data === null || data === undefined) return null;
74
+ if (typeof data === 'object' && !Array.isArray(data)) {
75
+ return { ...data };
76
+ }
77
+ return data;
78
+ }