@umituz/react-native-design-system 2.9.27 → 2.9.28

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.9.27",
3
+ "version": "2.9.28",
4
4
  "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Init Module Exports
3
+ */
4
+
5
+ export * from "../init";
package/src/index.ts CHANGED
@@ -130,3 +130,8 @@ export * from './exports/tanstack';
130
130
  // LOADING EXPORTS
131
131
  // =============================================================================
132
132
  export * from './exports/loading';
133
+
134
+ // =============================================================================
135
+ // INIT EXPORTS
136
+ // =============================================================================
137
+ export * from './exports/init';
@@ -0,0 +1,162 @@
1
+ /**
2
+ * App Initializer Factory
3
+ * Creates a singleton app initializer with ordered module execution
4
+ */
5
+
6
+ import type {
7
+ AppInitializerConfig,
8
+ AppInitializerResult,
9
+ InitModule,
10
+ } from "./types";
11
+
12
+ declare const __DEV__: boolean;
13
+
14
+ /**
15
+ * Create an app initializer with singleton pattern
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const initializeApp = createAppInitializer({
20
+ * modules: [
21
+ * { name: 'firebase', init: initFirebase, critical: true },
22
+ * { name: 'auth', init: initAuth, critical: true, dependsOn: ['firebase'] },
23
+ * { name: 'subscription', init: initSubscription, dependsOn: ['auth'] },
24
+ * { name: 'ai', init: initAI },
25
+ * ],
26
+ * });
27
+ *
28
+ * await initializeApp();
29
+ * ```
30
+ */
31
+ export function createAppInitializer(
32
+ config: AppInitializerConfig
33
+ ): () => Promise<AppInitializerResult> {
34
+ const {
35
+ modules,
36
+ debug = typeof __DEV__ !== "undefined" && __DEV__,
37
+ continueOnError = true,
38
+ onComplete,
39
+ onError,
40
+ } = config;
41
+
42
+ let initializationPromise: Promise<AppInitializerResult> | null = null;
43
+ let isInitialized = false;
44
+
45
+ const log = (message: string, ...args: unknown[]) => {
46
+ if (debug) {
47
+ console.log(`[AppInit] ${message}`, ...args);
48
+ }
49
+ };
50
+
51
+ const sortModulesByDependency = (mods: InitModule[]): InitModule[] => {
52
+ const sorted: InitModule[] = [];
53
+ const visited = new Set<string>();
54
+ const visiting = new Set<string>();
55
+
56
+ const visit = (mod: InitModule) => {
57
+ if (visited.has(mod.name)) return;
58
+ if (visiting.has(mod.name)) {
59
+ throw new Error(`Circular dependency detected: ${mod.name}`);
60
+ }
61
+
62
+ visiting.add(mod.name);
63
+
64
+ if (mod.dependsOn) {
65
+ for (const depName of mod.dependsOn) {
66
+ const dep = mods.find((m) => m.name === depName);
67
+ if (dep) visit(dep);
68
+ }
69
+ }
70
+
71
+ visiting.delete(mod.name);
72
+ visited.add(mod.name);
73
+ sorted.push(mod);
74
+ };
75
+
76
+ for (const mod of mods) {
77
+ visit(mod);
78
+ }
79
+
80
+ return sorted;
81
+ };
82
+
83
+ const performInitialization = async (): Promise<AppInitializerResult> => {
84
+ const startTime = Date.now();
85
+ const failedModules: string[] = [];
86
+
87
+ log("Starting initialization...");
88
+
89
+ const sortedModules = sortModulesByDependency(modules);
90
+
91
+ for (const mod of sortedModules) {
92
+ log(`Initializing: ${mod.name}`);
93
+
94
+ try {
95
+ const result = await mod.init();
96
+
97
+ if (result === false) {
98
+ throw new Error(`Module ${mod.name} returned false`);
99
+ }
100
+
101
+ log(`Completed: ${mod.name}`);
102
+ } catch (error) {
103
+ log(`Failed: ${mod.name}`, error);
104
+ failedModules.push(mod.name);
105
+
106
+ onError?.(mod.name, error);
107
+
108
+ if (mod.critical && !continueOnError) {
109
+ return {
110
+ success: false,
111
+ failedModules,
112
+ duration: Date.now() - startTime,
113
+ };
114
+ }
115
+ }
116
+ }
117
+
118
+ const duration = Date.now() - startTime;
119
+ const success = failedModules.length === 0;
120
+
121
+ log(`Initialization complete in ${duration}ms`, {
122
+ success,
123
+ failedModules,
124
+ });
125
+
126
+ isInitialized = true;
127
+ onComplete?.();
128
+
129
+ return { success, failedModules, duration };
130
+ };
131
+
132
+ return async () => {
133
+ if (isInitialized) {
134
+ return { success: true, failedModules: [], duration: 0 };
135
+ }
136
+
137
+ if (initializationPromise) {
138
+ return initializationPromise;
139
+ }
140
+
141
+ initializationPromise = performInitialization();
142
+ return initializationPromise;
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Create a simple initialization module
148
+ */
149
+ export function createInitModule(
150
+ name: string,
151
+ init: () => Promise<void> | void,
152
+ options?: Partial<Omit<InitModule, "name" | "init">>
153
+ ): InitModule {
154
+ return {
155
+ name,
156
+ init: async () => {
157
+ await init();
158
+ return true;
159
+ },
160
+ ...options,
161
+ };
162
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Environment Configuration Factory
3
+ * Creates type-safe environment accessors
4
+ */
5
+
6
+ import { Platform } from "react-native";
7
+ import type {
8
+ EnvConfig,
9
+ EnvAccessor,
10
+ EnvSourceConfig,
11
+ FirebaseEnvConfig,
12
+ } from "./types";
13
+
14
+ declare const __DEV__: boolean;
15
+
16
+ /**
17
+ * Default key mappings for environment variables
18
+ */
19
+ const DEFAULT_KEY_MAPPINGS: Record<string, string> = {
20
+ // RevenueCat
21
+ EXPO_PUBLIC_REVENUECAT_PROD_API_KEY_IOS: "revenueCatApiKeyIos",
22
+ EXPO_PUBLIC_REVENUECAT_PROD_API_KEY_ANDROID: "revenueCatApiKeyAndroid",
23
+ EXPO_PUBLIC_REVENUECAT_IOS_KEY: "revenueCatApiKeyIos",
24
+ EXPO_PUBLIC_REVENUECAT_ANDROID_KEY: "revenueCatApiKeyAndroid",
25
+ // FAL AI
26
+ EXPO_PUBLIC_FAL_AI_API_KEY: "falApiKey",
27
+ EXPO_PUBLIC_FAL_AI_BASE_URL: "falBaseUrl",
28
+ // Firebase
29
+ EXPO_PUBLIC_FIREBASE_API_KEY: "firebaseApiKey",
30
+ EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN: "firebaseAuthDomain",
31
+ EXPO_PUBLIC_FIREBASE_PROJECT_ID: "firebaseProjectId",
32
+ EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET: "firebaseStorageBucket",
33
+ EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: "firebaseMessagingSenderId",
34
+ EXPO_PUBLIC_FIREBASE_APP_ID: "firebaseAppId",
35
+ // OpenAI
36
+ EXPO_PUBLIC_OPENAI_API_KEY: "openAIApiKey",
37
+ };
38
+
39
+ /**
40
+ * Get environment value with fallback chain
41
+ */
42
+ function getEnvValue(
43
+ key: string,
44
+ sources: EnvSourceConfig,
45
+ keyMappings: Record<string, string>
46
+ ): string | undefined {
47
+ // 1. Try process.env first
48
+ if (sources.processEnv?.[key]) {
49
+ return sources.processEnv[key];
50
+ }
51
+
52
+ // 2. Try mapped extra key
53
+ const mappedKey = keyMappings[key];
54
+ if (mappedKey && sources.expoExtra) {
55
+ const value = sources.expoExtra[mappedKey];
56
+ // Filter out placeholder strings
57
+ if (typeof value === "string" && !value.startsWith("$EXPO_PUBLIC_")) {
58
+ return value;
59
+ }
60
+ }
61
+
62
+ return undefined;
63
+ }
64
+
65
+ /**
66
+ * Create environment configuration accessor
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * import Constants from 'expo-constants';
71
+ *
72
+ * const env = createEnvConfig({
73
+ * revenueCat: {
74
+ * testStoreKey: 'test_xxx', // Only used in __DEV__
75
+ * },
76
+ * fal: {
77
+ * baseUrl: 'https://fal.run',
78
+ * },
79
+ * }, {
80
+ * expoExtra: Constants.expoConfig?.extra,
81
+ * processEnv: process.env,
82
+ * });
83
+ *
84
+ * const apiKey = env.getRevenueCatApiKey();
85
+ * ```
86
+ */
87
+ export function createEnvConfig(
88
+ config: EnvConfig = {},
89
+ sources: EnvSourceConfig = {}
90
+ ): EnvAccessor {
91
+ // Default sources
92
+ const resolvedSources: EnvSourceConfig = {
93
+ expoExtra: sources.expoExtra ?? {},
94
+ processEnv: sources.processEnv ?? (process.env as Record<string, string>),
95
+ };
96
+
97
+ const get = (key: string): string | undefined => {
98
+ // Check custom config first
99
+ if (config.custom?.[key]) {
100
+ return config.custom[key];
101
+ }
102
+ return getEnvValue(key, resolvedSources, DEFAULT_KEY_MAPPINGS);
103
+ };
104
+
105
+ const getRevenueCatApiKey = (): string => {
106
+ const platformKey =
107
+ Platform.OS === "ios"
108
+ ? config.revenueCat?.iosKey ||
109
+ get("EXPO_PUBLIC_REVENUECAT_PROD_API_KEY_IOS") ||
110
+ get("EXPO_PUBLIC_REVENUECAT_IOS_KEY")
111
+ : config.revenueCat?.androidKey ||
112
+ get("EXPO_PUBLIC_REVENUECAT_PROD_API_KEY_ANDROID") ||
113
+ get("EXPO_PUBLIC_REVENUECAT_ANDROID_KEY");
114
+
115
+ return platformKey || "";
116
+ };
117
+
118
+ const getRevenueCatTestStoreKey = (): string | undefined => {
119
+ // Only return test store key in development
120
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
121
+ return config.revenueCat?.testStoreKey;
122
+ }
123
+ return undefined;
124
+ };
125
+
126
+ const getFalApiKey = (): string => {
127
+ return (
128
+ config.fal?.apiKey || get("EXPO_PUBLIC_FAL_AI_API_KEY") || ""
129
+ );
130
+ };
131
+
132
+ const getFalBaseUrl = (): string => {
133
+ return (
134
+ config.fal?.baseUrl ||
135
+ get("EXPO_PUBLIC_FAL_AI_BASE_URL") ||
136
+ "https://fal.run"
137
+ );
138
+ };
139
+
140
+ const getFirebaseConfig = (): FirebaseEnvConfig => ({
141
+ apiKey:
142
+ config.firebase?.apiKey || get("EXPO_PUBLIC_FIREBASE_API_KEY") || "",
143
+ authDomain:
144
+ config.firebase?.authDomain ||
145
+ get("EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN") ||
146
+ "",
147
+ projectId:
148
+ config.firebase?.projectId ||
149
+ get("EXPO_PUBLIC_FIREBASE_PROJECT_ID") ||
150
+ "",
151
+ storageBucket:
152
+ config.firebase?.storageBucket ||
153
+ get("EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET") ||
154
+ "",
155
+ messagingSenderId:
156
+ config.firebase?.messagingSenderId ||
157
+ get("EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID") ||
158
+ "",
159
+ appId: config.firebase?.appId || get("EXPO_PUBLIC_FIREBASE_APP_ID") || "",
160
+ });
161
+
162
+ return {
163
+ getRevenueCatApiKey,
164
+ getRevenueCatTestStoreKey,
165
+ getFalApiKey,
166
+ getFalBaseUrl,
167
+ getFirebaseConfig,
168
+ get,
169
+ };
170
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Environment Configuration
3
+ */
4
+
5
+ export { createEnvConfig } from "./createEnvConfig";
6
+ export type {
7
+ EnvConfig,
8
+ EnvAccessor,
9
+ EnvSourceConfig,
10
+ RevenueCatEnvConfig,
11
+ FalEnvConfig,
12
+ FirebaseEnvConfig,
13
+ } from "./types";
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Environment Configuration Types
3
+ */
4
+
5
+ /**
6
+ * Environment source configuration
7
+ */
8
+ export interface EnvSourceConfig {
9
+ /** Expo Constants extra config */
10
+ expoExtra?: Record<string, unknown>;
11
+ /** Process env (for EXPO_PUBLIC_ vars) */
12
+ processEnv?: Record<string, string | undefined>;
13
+ }
14
+
15
+ /**
16
+ * Key mapping from EXPO_PUBLIC_ to extra key
17
+ */
18
+ export interface EnvKeyMapping {
19
+ /** Environment variable name (e.g., 'EXPO_PUBLIC_FAL_API_KEY') */
20
+ envKey: string;
21
+ /** Extra config key (e.g., 'falApiKey') */
22
+ extraKey: string;
23
+ }
24
+
25
+ /**
26
+ * RevenueCat configuration
27
+ */
28
+ export interface RevenueCatEnvConfig {
29
+ /** iOS API key */
30
+ iosKey?: string;
31
+ /** Android API key */
32
+ androidKey?: string;
33
+ /** Test store key (for development) */
34
+ testStoreKey?: string;
35
+ }
36
+
37
+ /**
38
+ * FAL AI configuration
39
+ */
40
+ export interface FalEnvConfig {
41
+ /** API key */
42
+ apiKey?: string;
43
+ /** Base URL (default: https://fal.run) */
44
+ baseUrl?: string;
45
+ }
46
+
47
+ /**
48
+ * Firebase configuration
49
+ */
50
+ export interface FirebaseEnvConfig {
51
+ apiKey?: string;
52
+ authDomain?: string;
53
+ projectId?: string;
54
+ storageBucket?: string;
55
+ messagingSenderId?: string;
56
+ appId?: string;
57
+ }
58
+
59
+ /**
60
+ * Full environment configuration
61
+ */
62
+ export interface EnvConfig {
63
+ revenueCat?: RevenueCatEnvConfig;
64
+ fal?: FalEnvConfig;
65
+ firebase?: FirebaseEnvConfig;
66
+ /** Custom keys */
67
+ custom?: Record<string, string | undefined>;
68
+ }
69
+
70
+ /**
71
+ * Created environment accessor
72
+ */
73
+ export interface EnvAccessor {
74
+ /** Get RevenueCat API key (platform-specific) */
75
+ getRevenueCatApiKey: () => string;
76
+ /** Get RevenueCat test store key (dev only) */
77
+ getRevenueCatTestStoreKey: () => string | undefined;
78
+ /** Get FAL API key */
79
+ getFalApiKey: () => string;
80
+ /** Get FAL base URL */
81
+ getFalBaseUrl: () => string;
82
+ /** Get Firebase config */
83
+ getFirebaseConfig: () => FirebaseEnvConfig;
84
+ /** Get custom env value */
85
+ get: (key: string) => string | undefined;
86
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * App Initialization Module
3
+ *
4
+ * Provides utilities for app initialization:
5
+ * - createAppInitializer: Factory for creating app initializers
6
+ * - useAppInitialization: Hook for managing initialization state
7
+ * - createEnvConfig: Environment configuration factory
8
+ */
9
+
10
+ // App Initializer
11
+ export {
12
+ createAppInitializer,
13
+ createInitModule,
14
+ } from "./createAppInitializer";
15
+
16
+ // Initialization Hook
17
+ export { useAppInitialization } from "./useAppInitialization";
18
+
19
+ // Environment Configuration
20
+ export * from "./env";
21
+
22
+ // Types
23
+ export type {
24
+ InitModule,
25
+ AppInitializerConfig,
26
+ AppInitializerResult,
27
+ UseAppInitializationOptions,
28
+ UseAppInitializationReturn,
29
+ } from "./types";
@@ -0,0 +1,63 @@
1
+ /**
2
+ * App Initialization Types
3
+ */
4
+
5
+ /**
6
+ * Single initialization module
7
+ */
8
+ export interface InitModule {
9
+ /** Module name for logging */
10
+ name: string;
11
+ /** Initialize function - returns success status */
12
+ init: () => Promise<boolean> | boolean;
13
+ /** Optional: Whether this module is critical (default: false) */
14
+ critical?: boolean;
15
+ /** Optional: Dependencies - other module names that must run first */
16
+ dependsOn?: string[];
17
+ }
18
+
19
+ /**
20
+ * App initializer configuration
21
+ */
22
+ export interface AppInitializerConfig {
23
+ /** Initialization modules to run */
24
+ modules: InitModule[];
25
+ /** Enable debug logging (default: __DEV__) */
26
+ debug?: boolean;
27
+ /** Continue on non-critical module failure (default: true) */
28
+ continueOnError?: boolean;
29
+ /** Callback when all modules complete */
30
+ onComplete?: () => void;
31
+ /** Callback on error */
32
+ onError?: (moduleName: string, error: unknown) => void;
33
+ }
34
+
35
+ /**
36
+ * App initializer result
37
+ */
38
+ export interface AppInitializerResult {
39
+ success: boolean;
40
+ failedModules: string[];
41
+ duration: number;
42
+ }
43
+
44
+ /**
45
+ * useAppInitialization hook options
46
+ */
47
+ export interface UseAppInitializationOptions {
48
+ /** Skip initialization (useful for testing) */
49
+ skip?: boolean;
50
+ /** Callback when initialization completes */
51
+ onReady?: () => void;
52
+ /** Callback on error */
53
+ onError?: (error: Error) => void;
54
+ }
55
+
56
+ /**
57
+ * useAppInitialization hook return type
58
+ */
59
+ export interface UseAppInitializationReturn {
60
+ isReady: boolean;
61
+ isLoading: boolean;
62
+ error: Error | null;
63
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * useAppInitialization Hook
3
+ * Manages app initialization state in React components
4
+ */
5
+
6
+ import { useState, useEffect, useCallback } from "react";
7
+ import type {
8
+ UseAppInitializationOptions,
9
+ UseAppInitializationReturn,
10
+ AppInitializerResult,
11
+ } from "./types";
12
+
13
+ /**
14
+ * Hook to manage app initialization
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const { isReady, isLoading, error } = useAppInitialization(initializeApp, {
19
+ * onReady: () => console.log('App ready!'),
20
+ * });
21
+ *
22
+ * if (isLoading) return <SplashScreen />;
23
+ * if (error) return <ErrorScreen error={error} />;
24
+ * return <App />;
25
+ * ```
26
+ */
27
+ export function useAppInitialization(
28
+ initializer: () => Promise<AppInitializerResult>,
29
+ options: UseAppInitializationOptions = {}
30
+ ): UseAppInitializationReturn {
31
+ const { skip = false, onReady, onError } = options;
32
+
33
+ const [isReady, setIsReady] = useState(false);
34
+ const [isLoading, setIsLoading] = useState(!skip);
35
+ const [error, setError] = useState<Error | null>(null);
36
+
37
+ const initialize = useCallback(async () => {
38
+ if (skip) {
39
+ setIsReady(true);
40
+ setIsLoading(false);
41
+ return;
42
+ }
43
+
44
+ try {
45
+ setIsLoading(true);
46
+ setError(null);
47
+
48
+ const result = await initializer();
49
+
50
+ if (!result.success && result.failedModules.length > 0) {
51
+ const criticalFailed = result.failedModules.length > 0;
52
+ if (criticalFailed) {
53
+ throw new Error(
54
+ `Initialization failed: ${result.failedModules.join(", ")}`
55
+ );
56
+ }
57
+ }
58
+
59
+ setIsReady(true);
60
+ onReady?.();
61
+ } catch (err) {
62
+ const error = err instanceof Error ? err : new Error(String(err));
63
+ setError(error);
64
+ onError?.(error);
65
+ } finally {
66
+ setIsLoading(false);
67
+ }
68
+ }, [initializer, skip, onReady, onError]);
69
+
70
+ useEffect(() => {
71
+ initialize();
72
+ }, [initialize]);
73
+
74
+ return { isReady, isLoading, error };
75
+ }