@sudobility/di 1.5.0 → 1.5.3

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/README.md CHANGED
@@ -1,67 +1,283 @@
1
- # @johnqh/di
1
+ # @sudobility/di
2
2
 
3
- Platform-agnostic dependency injection interfaces for React and React Native projects.
3
+ Platform-agnostic dependency injection for React and React Native with automatic platform detection.
4
4
 
5
5
  ## Overview
6
6
 
7
- This library provides abstract TypeScript interfaces for dependency injection patterns that work across web and React Native platforms. It contains no platform-specific code and focuses purely on defining contracts for common services.
7
+ This library provides unified DI services that work identically on Web and React Native. The bundler automatically selects the correct platform implementation, so you write the same code for both platforms.
8
8
 
9
9
  ## Features
10
10
 
11
- - 🚀 **Platform Agnostic**: Works with both React and React Native
12
- - 📱 **React Native Compatible**: No web-specific dependencies
13
- - 🔌 **Dependency Injection**: Clean interface-based architecture
14
- - 🎯 **TypeScript First**: Fully typed interfaces
15
- - 🏗️ **Modular**: Import only what you need
11
+ - **Unified API**: Same code works on Web and React Native
12
+ - **Automatic Platform Detection**: Bundler selects correct implementation
13
+ - **TypeScript First**: Fully typed interfaces and implementations
14
+ - **Lazy Loading**: Native modules loaded only when used
15
+ - **Singleton Management**: Built-in service lifecycle management
16
+ - **Modular**: Import only what you need
16
17
 
17
18
  ## Installation
18
19
 
19
20
  ```bash
20
- npm install @johnqh/di
21
+ npm install @sudobility/di
21
22
  ```
22
23
 
23
- ## Usage
24
+ ### Optional Peer Dependencies
25
+
26
+ **Web:**
27
+ ```bash
28
+ npm install firebase
29
+ ```
30
+
31
+ **React Native:**
32
+ ```bash
33
+ npm install @react-native-async-storage/async-storage
34
+ npm install @react-native-community/netinfo
35
+ npm install @react-native-firebase/app @react-native-firebase/analytics
36
+ npm install @react-native-firebase/remote-config @react-native-firebase/messaging
37
+ npm install @notifee/react-native
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ### 1. Initialize Services
24
43
 
25
44
  ```typescript
45
+ // main.tsx or App.tsx
26
46
  import {
27
- NetworkClient,
28
- StorageProvider,
29
- AnalyticsService,
30
- EnvProvider
31
- } from '@johnqh/di';
32
-
33
- // Implement interfaces for your specific platform
34
- class MyNetworkClient implements NetworkClient {
35
- // Your implementation
36
- }
47
+ initializeStorageService,
48
+ initializeNetworkService,
49
+ initializeFirebaseService,
50
+ } from '@sudobility/di';
51
+
52
+ // Call before rendering app
53
+ initializeStorageService();
54
+ initializeNetworkService();
55
+ initializeFirebaseService(firebaseConfig);
37
56
  ```
38
57
 
39
- ## Included Interfaces
58
+ ### 2. Use Services
40
59
 
41
- ### Network
42
- - `NetworkClient` - HTTP client interface
43
- - `NetworkResponse` - Response type
44
- - `NetworkRequestOptions` - Request configuration
60
+ ```typescript
61
+ import {
62
+ getStorageService,
63
+ getNetworkService,
64
+ getFirebaseService,
65
+ networkClient,
66
+ } from '@sudobility/di';
67
+
68
+ // Storage
69
+ const storage = getStorageService();
70
+ await storage.setItem('token', 'abc123');
71
+ const token = await storage.getItem('token');
72
+
73
+ // Network requests
74
+ const response = await networkClient.get<User>('/api/user');
75
+
76
+ // Network status
77
+ const network = getNetworkService();
78
+ const isOnline = network.isOnline();
79
+
80
+ // Firebase
81
+ const firebase = getFirebaseService();
82
+ firebase.analytics.logEvent('screen_view', { screen: 'Home' });
83
+ ```
84
+
85
+ ## API Reference
45
86
 
46
87
  ### Storage
47
- - `StorageProvider` - Storage abstraction
48
- - `PlatformStorage` - Basic storage interface
49
- - `StorageService` - Enhanced storage service
50
88
 
51
- ### Analytics
52
- - `AnalyticsService` - Analytics tracking interface
53
- - `EmailAnalyticsService` - Email-specific analytics
89
+ ```typescript
90
+ import { getStorageService, storage } from '@sudobility/di';
91
+
92
+ const storageService = getStorageService();
93
+
94
+ // Basic operations
95
+ await storageService.setItem('key', 'value');
96
+ const value = await storageService.getItem('key');
97
+ await storageService.removeItem('key');
98
+ await storageService.clear();
99
+ const keys = await storageService.getAllKeys();
100
+ ```
101
+
102
+ ### Network Requests
103
+
104
+ ```typescript
105
+ import { networkClient } from '@sudobility/di';
106
+
107
+ // GET
108
+ const response = await networkClient.get<User>('/api/user');
109
+ if (response.ok) {
110
+ console.log(response.data);
111
+ }
112
+
113
+ // POST with body
114
+ const result = await networkClient.post<Result>('/api/submit', {
115
+ name: 'John',
116
+ email: 'john@example.com'
117
+ });
118
+
119
+ // PUT and DELETE
120
+ await networkClient.put('/api/user/1', { name: 'Jane' });
121
+ await networkClient.delete('/api/user/1');
54
122
 
55
- ### Environment
56
- - `EnvProvider` - Environment configuration
57
- - `AppConfig` - Application configuration
123
+ // With options
124
+ const response = await networkClient.get('/api/data', {
125
+ headers: { 'Authorization': 'Bearer token' },
126
+ timeout: 5000,
127
+ });
128
+ ```
129
+
130
+ ### Network Status
131
+
132
+ ```typescript
133
+ import { getNetworkService } from '@sudobility/di';
134
+
135
+ const network = getNetworkService();
136
+
137
+ // Check connection
138
+ const isOnline = network.isOnline();
139
+
140
+ // Watch for changes
141
+ const unsubscribe = network.watchNetworkStatus((online) => {
142
+ console.log('Connection:', online ? 'Online' : 'Offline');
143
+ });
144
+
145
+ // Cleanup when done
146
+ unsubscribe();
147
+ ```
58
148
 
59
- ### Authentication
60
- - Auth interfaces for various providers
149
+ ### Firebase
61
150
 
62
- ### Navigation & Notifications
63
- - Platform-agnostic navigation and notification interfaces
151
+ ```typescript
152
+ import { getFirebaseService } from '@sudobility/di';
153
+
154
+ const firebase = getFirebaseService();
155
+
156
+ // Analytics
157
+ firebase.analytics.logEvent('purchase', { item: 'premium', price: 9.99 });
158
+ firebase.analytics.setUserId('user123');
159
+ firebase.analytics.setUserProperties({ plan: 'pro' });
160
+
161
+ // Remote Config
162
+ await firebase.remoteConfig.fetchAndActivate();
163
+ const showFeature = firebase.remoteConfig.getValue('new_feature').asBoolean();
164
+ const apiUrl = firebase.remoteConfig.getValue('api_url').asString();
165
+ const maxItems = firebase.remoteConfig.getValue('max_items').asNumber();
166
+
167
+ // Messaging (FCM)
168
+ const granted = await firebase.messaging.requestPermission();
169
+ if (granted) {
170
+ const token = await firebase.messaging.getToken();
171
+ console.log('FCM Token:', token);
172
+ }
173
+
174
+ const unsubscribe = firebase.messaging.onMessage((message) => {
175
+ console.log('Push notification:', message.notification?.title);
176
+ });
177
+ ```
178
+
179
+ ### React Native Only Services
180
+
181
+ ```typescript
182
+ import {
183
+ getThemeService,
184
+ getNavigationService,
185
+ getNotificationService,
186
+ } from '@sudobility/di';
187
+
188
+ // Theme
189
+ const theme = getThemeService();
190
+ const isDark = theme.isDarkMode();
191
+ theme.applyTheme('dark'); // 'light' | 'dark' | 'system'
192
+ theme.watchSystemTheme((mode) => {
193
+ console.log('System theme:', mode);
194
+ });
195
+
196
+ // Navigation (call setNavigationRef first in App.tsx)
197
+ const nav = getNavigationService();
198
+ nav.navigate('Profile', { userId: '123' });
199
+ nav.goBack();
200
+ nav.replace('Home');
201
+
202
+ // Notifications
203
+ const notifications = getNotificationService();
204
+ await notifications.requestPermission();
205
+ await notifications.showNotification('Hello', {
206
+ body: 'You have a new message',
207
+ data: { screen: 'inbox' }
208
+ });
209
+ await notifications.setBadgeCount(5);
210
+ ```
211
+
212
+ ## Import Paths
213
+
214
+ ```typescript
215
+ // Auto-detect platform (recommended)
216
+ import { ... } from '@sudobility/di';
217
+
218
+ // Explicit web imports
219
+ import { ... } from '@sudobility/di/web';
220
+
221
+ // Explicit React Native imports
222
+ import { ... } from '@sudobility/di/rn';
223
+
224
+ // Interfaces only (no implementations)
225
+ import type { ... } from '@sudobility/di/interfaces';
226
+
227
+ // Mocks for testing
228
+ import { MockStorageService, MockNetworkClient } from '@sudobility/di/mocks';
229
+ ```
230
+
231
+ ## Unified Exports
232
+
233
+ These exports work identically on both platforms:
234
+
235
+ | Export | Description |
236
+ |--------|-------------|
237
+ | `networkClient` | Pre-configured network client instance |
238
+ | `storage` | Basic storage instance |
239
+ | `advancedStorage` | Storage with TTL support |
240
+ | `getStorageService()` | Get storage service singleton |
241
+ | `getNetworkService()` | Get network service singleton |
242
+ | `getFirebaseService()` | Get Firebase service singleton |
243
+ | `initializeStorageService()` | Initialize storage singleton |
244
+ | `initializeNetworkService()` | Initialize network singleton |
245
+ | `initializeFirebaseService()` | Initialize Firebase singleton |
246
+ | `StorageService` | Storage service class |
247
+ | `NetworkService` | Network service class |
248
+ | `FirebaseService` | Firebase service class |
249
+
250
+ ## Platform-Specific Exports
251
+
252
+ ### Web Only
253
+ - `WebStorageService`, `WebNetworkService`, `WebFirebaseService`
254
+ - `webStorage`, `webNetworkClient`
255
+ - `WebUINavigationService`
256
+
257
+ ### React Native Only
258
+ - `RNStorageService`, `RNNetworkService`, `RNFirebaseService`
259
+ - `rnStorage`, `rnNetworkClient`
260
+ - `getThemeService()`, `getNavigationService()`, `getNotificationService()`
261
+ - `RNThemeService`, `RNNavigationService`, `RNNotificationService`
262
+
263
+ ## Testing with Mocks
264
+
265
+ ```typescript
266
+ import {
267
+ MockStorageService,
268
+ MockNetworkClient,
269
+ MockAnalyticsClient,
270
+ } from '@sudobility/di/mocks';
271
+
272
+ // In your tests
273
+ const mockStorage = new MockStorageService();
274
+ await mockStorage.setItem('key', 'value');
275
+ expect(await mockStorage.getItem('key')).toBe('value');
276
+
277
+ const mockNetwork = new MockNetworkClient();
278
+ // Configure mock responses...
279
+ ```
64
280
 
65
281
  ## License
66
282
 
67
- MIT
283
+ MIT
@@ -0,0 +1,109 @@
1
+ import type { AnalyticsService, RemoteConfigService, RemoteConfigValue, FCMService, FCMMessage, FCMPermissionState, FirebaseService, FirebaseConfig, FirebaseInitOptions } from '../web/firebase/firebase.interface.js';
2
+ export interface RecordedFirebaseAnalyticsEvent {
3
+ type: 'event' | 'userProperty' | 'userId';
4
+ name?: string;
5
+ parameters?: Record<string, unknown>;
6
+ properties?: Record<string, string>;
7
+ userId?: string;
8
+ timestamp: number;
9
+ }
10
+ export declare class MockAnalyticsService implements AnalyticsService {
11
+ private events;
12
+ private supported;
13
+ private currentUserId;
14
+ private userProperties;
15
+ logEvent(eventName: string, parameters?: Record<string, unknown>): void;
16
+ setUserProperties(properties: Record<string, string>): void;
17
+ setUserId(userId: string): void;
18
+ isSupported(): boolean;
19
+ setSupported(supported: boolean): void;
20
+ getEvents(): RecordedFirebaseAnalyticsEvent[];
21
+ getEventsByType(type: 'event' | 'userProperty' | 'userId'): RecordedFirebaseAnalyticsEvent[];
22
+ getEventsByName(name: string): RecordedFirebaseAnalyticsEvent[];
23
+ getUserId(): string | null;
24
+ getUserProperties(): Record<string, string>;
25
+ reset(): void;
26
+ }
27
+ export declare class MockRemoteConfigValue implements RemoteConfigValue {
28
+ private value;
29
+ private source;
30
+ constructor(value: string, source?: 'static' | 'default' | 'remote');
31
+ asBoolean(): boolean;
32
+ asString(): string;
33
+ asNumber(): number;
34
+ getSource(): 'static' | 'default' | 'remote';
35
+ }
36
+ export declare class MockRemoteConfigService implements RemoteConfigService {
37
+ private configs;
38
+ private supported;
39
+ private fetchDelay;
40
+ private fetchShouldFail;
41
+ private fetchError;
42
+ fetchAndActivate(): Promise<boolean>;
43
+ getValue(key: string): RemoteConfigValue;
44
+ getAll(): Record<string, RemoteConfigValue>;
45
+ isSupported(): boolean;
46
+ setSupported(supported: boolean): void;
47
+ setConfig(key: string, value: string, source?: 'static' | 'default' | 'remote'): void;
48
+ setConfigs(configs: Record<string, string>): void;
49
+ setFetchDelay(delay: number): void;
50
+ setFetchShouldFail(shouldFail: boolean, error?: Error): void;
51
+ clearConfig(key: string): void;
52
+ reset(): void;
53
+ }
54
+ export interface RecordedFCMEvent {
55
+ type: 'permissionRequest' | 'getToken' | 'deleteToken' | 'messageReceived';
56
+ token?: string | null;
57
+ message?: FCMMessage;
58
+ timestamp: number;
59
+ }
60
+ export declare class MockFCMService implements FCMService {
61
+ private events;
62
+ private supported;
63
+ private permissionStatus;
64
+ private mockToken;
65
+ private messageListeners;
66
+ private permissionRequestDelay;
67
+ private tokenRequestDelay;
68
+ private shouldFailPermission;
69
+ private shouldFailToken;
70
+ requestPermission(): Promise<boolean>;
71
+ getToken(): Promise<string | null>;
72
+ deleteToken(): Promise<boolean>;
73
+ onMessage(callback: (message: FCMMessage) => void): () => void;
74
+ isSupported(): boolean;
75
+ getPermissionStatus(): FCMPermissionState;
76
+ setSupported(supported: boolean): void;
77
+ setToken(token: string | null): void;
78
+ setPermissionGranted(granted: boolean): void;
79
+ setPermissionRequestDelay(delay: number): void;
80
+ setTokenRequestDelay(delay: number): void;
81
+ setShouldFailPermission(shouldFail: boolean): void;
82
+ setShouldFailToken(shouldFail: boolean): void;
83
+ simulateMessage(message: FCMMessage): void;
84
+ getEvents(): RecordedFCMEvent[];
85
+ getEventsByType(type: 'permissionRequest' | 'getToken' | 'deleteToken' | 'messageReceived'): RecordedFCMEvent[];
86
+ getListenerCount(): number;
87
+ reset(): void;
88
+ }
89
+ export declare class MockFirebaseService implements FirebaseService {
90
+ analytics: MockAnalyticsService;
91
+ remoteConfig: MockRemoteConfigService;
92
+ messaging: MockFCMService;
93
+ private configured;
94
+ private developmentMode;
95
+ private config;
96
+ private options;
97
+ constructor(config?: FirebaseConfig, options?: FirebaseInitOptions);
98
+ isConfigured(): boolean;
99
+ isDevelopment(): boolean;
100
+ setConfigured(configured: boolean): void;
101
+ setDevelopmentMode(developmentMode: boolean): void;
102
+ getConfig(): FirebaseConfig | null;
103
+ getOptions(): FirebaseInitOptions | null;
104
+ reset(): void;
105
+ }
106
+ export declare function getMockFirebaseService(): MockFirebaseService;
107
+ export declare function initializeMockFirebaseService(config?: FirebaseConfig, options?: FirebaseInitOptions): MockFirebaseService;
108
+ export declare function resetMockFirebaseService(): void;
109
+ //# sourceMappingURL=firebase.mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firebase.mock.d.ts","sourceRoot":"","sources":["../../src/mocks/firebase.mock.ts"],"names":[],"mappings":"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"}