@umituz/react-native-subscription 2.27.112 → 2.27.114

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 (61) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/application/CreditsInitializer.ts +28 -125
  3. package/src/domains/credits/application/credit-strategies/{CreditAllocationContext.ts → CreditAllocationOrchestrator.ts} +4 -9
  4. package/src/domains/credits/application/creditDocumentHelpers.ts +58 -0
  5. package/src/domains/credits/application/creditOperationUtils.ts +154 -0
  6. package/src/domains/credits/core/CreditsMapper.ts +8 -13
  7. package/src/domains/credits/infrastructure/{CreditsRepositoryProvider.ts → CreditsRepositoryManager.ts} +2 -2
  8. package/src/domains/credits/presentation/useCredits.ts +2 -3
  9. package/src/domains/credits/presentation/useDeductCredit.ts +4 -4
  10. package/src/domains/paywall/components/PaywallContainer.types.ts +1 -1
  11. package/src/domains/paywall/components/PaywallModal.tsx +28 -52
  12. package/src/domains/paywall/hooks/usePaywallActions.ts +77 -33
  13. package/src/domains/subscription/application/SubscriptionInitializer.ts +1 -1
  14. package/src/domains/subscription/application/SubscriptionSyncService.ts +17 -21
  15. package/src/domains/subscription/core/RevenueCatError.ts +40 -31
  16. package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +0 -1
  17. package/src/domains/subscription/infrastructure/hooks/useRevenueCatTrialEligibility.ts +19 -85
  18. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +33 -75
  19. package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +57 -0
  20. package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +3 -12
  21. package/src/domains/subscription/infrastructure/services/PurchaseHandler.ts +0 -2
  22. package/src/domains/subscription/infrastructure/services/RevenueCatInitializer.ts +2 -4
  23. package/src/domains/subscription/infrastructure/services/RevenueCatService.ts +1 -5
  24. package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +3 -12
  25. package/src/domains/subscription/infrastructure/utils/authPurchaseState.ts +69 -0
  26. package/src/domains/subscription/infrastructure/utils/trialEligibilityUtils.ts +77 -0
  27. package/src/domains/subscription/presentation/components/feedback/FeedbackOption.tsx +139 -0
  28. package/src/domains/subscription/presentation/components/feedback/PaywallFeedbackModal.tsx +15 -70
  29. package/src/domains/subscription/presentation/components/feedback/paywallFeedbackStyles.ts +0 -92
  30. package/src/domains/subscription/presentation/screens/SubscriptionDetailScreen.tsx +1 -1
  31. package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +1 -18
  32. package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +19 -69
  33. package/src/domains/subscription/presentation/usePaywallVisibility.ts +1 -1
  34. package/src/domains/subscription/presentation/usePremium.ts +2 -11
  35. package/src/domains/subscription/presentation/useSubscriptionStatus.ts +1 -6
  36. package/src/domains/trial/application/TrialService.ts +4 -8
  37. package/src/domains/wallet/index.ts +0 -6
  38. package/src/domains/wallet/infrastructure/repositories/TransactionRepository.ts +1 -1
  39. package/src/domains/wallet/infrastructure/services/ProductMetadataService.ts +0 -13
  40. package/src/domains/wallet/presentation/hooks/useProductMetadata.ts +0 -10
  41. package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +0 -8
  42. package/src/domains/wallet/presentation/screens/WalletScreen.tsx +57 -43
  43. package/src/index.ts +1 -1
  44. package/src/init/createSubscriptionInitModule.ts +1 -4
  45. package/src/presentation/hooks/feedback/useFeedbackSubmit.ts +0 -14
  46. package/src/shared/application/ActivationHandler.ts +6 -6
  47. package/src/shared/application/FeedbackService.ts +0 -21
  48. package/src/shared/infrastructure/SubscriptionEventBus.ts +1 -2
  49. package/src/shared/presentation/index.ts +1 -0
  50. package/src/shared/presentation/layouts/ScreenLayout.tsx +79 -0
  51. package/src/shared/types/CommonTypes.ts +65 -0
  52. package/src/shared/utils/BaseError.ts +26 -0
  53. package/src/shared/utils/Logger.ts +15 -46
  54. package/src/shared/utils/Result.ts +16 -0
  55. package/src/shared/utils/SubscriptionConfig.ts +1 -1
  56. package/src/shared/utils/SubscriptionError.ts +20 -30
  57. package/src/utils/appUtils.ts +34 -0
  58. package/src/utils/dateUtils.ts +32 -0
  59. package/src/utils/index.ts +2 -0
  60. package/src/utils/packageTypeDetector.ts +0 -4
  61. package/src/domains/wallet/presentation/screens/WalletScreenContainer.tsx +0 -88
@@ -9,13 +9,15 @@
9
9
  import React from "react";
10
10
  import { View, StyleSheet, TouchableOpacity } from "react-native";
11
11
  import {
12
- useSafeAreaInsets,
13
12
  useAppDesignTokens,
14
13
  AtomicText,
15
14
  AtomicIcon,
16
15
  AtomicSpinner,
17
- ScreenLayout,
18
16
  } from "@umituz/react-native-design-system";
17
+ import { ScreenLayout } from "../../../../shared/presentation";
18
+ import { useNavigation } from "@react-navigation/native";
19
+ import { useWallet } from "../hooks/useWallet";
20
+ import { getWalletConfig } from "../../infrastructure/config/walletConfig";
19
21
  import {
20
22
  BalanceCard,
21
23
  type BalanceCardTranslations,
@@ -24,7 +26,6 @@ import {
24
26
  TransactionList,
25
27
  type TransactionListTranslations,
26
28
  } from "../components/TransactionList";
27
- import type { CreditLog } from "../../domain/types/transaction.types";
28
29
 
29
30
  export interface WalletScreenTranslations
30
31
  extends BalanceCardTranslations,
@@ -32,58 +33,72 @@ export interface WalletScreenTranslations
32
33
  screenTitle: string;
33
34
  }
34
35
 
35
- export interface WalletScreenConfig {
36
- balance: number;
37
- balanceLoading: boolean;
38
- transactions: CreditLog[];
39
- transactionsLoading: boolean;
40
- translations: WalletScreenTranslations;
36
+ export interface WalletScreenProps {
37
+ /** Translations (overrides global config) */
38
+ translations?: WalletScreenTranslations;
39
+ /** Override onBack handler (default: navigation.goBack) */
41
40
  onBack?: () => void;
41
+ /** Custom date formatter */
42
42
  dateFormatter?: (timestamp: number) => string;
43
- maxTransactionHeight?: number;
44
- balanceIconName?: string;
43
+ /** Footer component */
45
44
  footer?: React.ReactNode;
46
45
  }
47
46
 
48
- export interface WalletScreenProps {
49
- config: WalletScreenConfig;
50
- }
51
-
52
- export const WalletScreen: React.FC<WalletScreenProps> = ({ config }) => {
47
+ export const WalletScreen: React.FC<WalletScreenProps> = ({
48
+ translations,
49
+ onBack,
50
+ dateFormatter,
51
+ footer,
52
+ }) => {
53
53
  const tokens = useAppDesignTokens();
54
- const insets = useSafeAreaInsets();
54
+ const navigation = useNavigation();
55
+ const config = getWalletConfig();
56
+
57
+ const {
58
+ balance,
59
+ balanceLoading,
60
+ transactions,
61
+ transactionsLoading,
62
+ } = useWallet({
63
+ transactionConfig: {
64
+ collectionName: config.transactionCollection,
65
+ useUserSubcollection: config.useUserSubcollection,
66
+ },
67
+ transactionLimit: config.transactionLimit,
68
+ });
69
+
70
+ const activeTranslations = translations ?? config.translations;
71
+ const handleBack = onBack ?? (() => navigation.goBack());
55
72
 
56
73
  const renderHeader = () => (
57
- <View style={[styles.header, { paddingTop: insets.top + 12 }]}>
58
- {config.onBack && (
59
- <TouchableOpacity
60
- onPress={config.onBack}
61
- style={styles.backButton}
62
- hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
63
- >
64
- <AtomicIcon
65
- name="arrow-left"
66
- size="lg"
67
- customColor={tokens.colors.textPrimary}
68
- />
69
- </TouchableOpacity>
70
- )}
74
+ <View style={[styles.header, { paddingTop: 12 }]}>
75
+ <TouchableOpacity
76
+ onPress={handleBack}
77
+ style={styles.backButton}
78
+ hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
79
+ >
80
+ <AtomicIcon
81
+ name="arrow-left"
82
+ size="lg"
83
+ customColor={tokens.colors.textPrimary}
84
+ />
85
+ </TouchableOpacity>
71
86
  <AtomicText
72
87
  type="titleLarge"
73
88
  style={{ color: tokens.colors.textPrimary, fontWeight: "700" }}
74
89
  >
75
- {config.translations.screenTitle}
90
+ {activeTranslations.screenTitle}
76
91
  </AtomicText>
77
92
  </View>
78
93
  );
79
94
 
80
95
  const renderBalance = () => {
81
- if (config.balanceLoading) {
96
+ if (balanceLoading) {
82
97
  return (
83
98
  <AtomicSpinner
84
99
  size="xl"
85
100
  color="primary"
86
- text={config.translations.loading}
101
+ text={activeTranslations.loading}
87
102
  fullContainer
88
103
  style={styles.loadingContainer}
89
104
  />
@@ -92,8 +107,8 @@ export const WalletScreen: React.FC<WalletScreenProps> = ({ config }) => {
92
107
 
93
108
  return (
94
109
  <BalanceCard
95
- balance={config.balance}
96
- translations={config.translations}
110
+ balance={balance}
111
+ translations={activeTranslations}
97
112
  iconName={config.balanceIconName}
98
113
  />
99
114
  );
@@ -102,19 +117,18 @@ export const WalletScreen: React.FC<WalletScreenProps> = ({ config }) => {
102
117
  return (
103
118
  <ScreenLayout
104
119
  scrollable={true}
105
- edges={["bottom"]}
120
+ edges={["top", "bottom"]}
106
121
  backgroundColor={tokens.colors.backgroundPrimary}
107
122
  contentContainerStyle={styles.content}
108
- footer={config.footer}
123
+ footer={footer}
109
124
  >
110
125
  {renderHeader()}
111
126
  {renderBalance()}
112
127
  <TransactionList
113
- transactions={config.transactions}
114
- loading={config.transactionsLoading}
115
- translations={config.translations}
116
- maxHeight={config.maxTransactionHeight}
117
- dateFormatter={config.dateFormatter}
128
+ transactions={transactions}
129
+ loading={transactionsLoading}
130
+ translations={activeTranslations}
131
+ dateFormatter={dateFormatter}
118
132
  />
119
133
  </ScreenLayout>
120
134
  );
package/src/index.ts CHANGED
@@ -48,7 +48,7 @@ export {
48
48
  getCreditsRepository,
49
49
  getCreditsConfig,
50
50
  isCreditsRepositoryConfigured
51
- } from "./domains/credits/infrastructure/CreditsRepositoryProvider";
51
+ } from "./domains/credits/infrastructure/CreditsRepositoryManager";
52
52
 
53
53
  // Presentation Layer - Hooks (Point to the bridge)
54
54
  export * from "./presentation/hooks";
@@ -18,15 +18,12 @@ export function createSubscriptionInitModule(config: SubscriptionInitModuleConfi
18
18
  try {
19
19
  const apiKey = getApiKey();
20
20
  if (!apiKey) {
21
- if (__DEV__) console.log('[SubscriptionInit] No API key - skipping');
22
21
  return true;
23
22
  }
24
23
 
25
24
  await initializeSubscription({ apiKey, ...subscriptionConfig });
26
- if (__DEV__) console.log('[SubscriptionInit] Initialized');
27
25
  return true;
28
- } catch (error) {
29
- if (__DEV__) console.error('[SubscriptionInit] Error:', error);
26
+ } catch {
30
27
  return false;
31
28
  }
32
29
  },
@@ -27,13 +27,6 @@ export function usePaywallFeedbackSubmit(
27
27
 
28
28
  const submit = useCallback(
29
29
  async (reason: string) => {
30
- if (__DEV__) {
31
- console.log("[usePaywallFeedbackSubmit] Submitting:", {
32
- userId: user?.uid?.slice(0, 8),
33
- reason: reason.slice(0, 20),
34
- });
35
- }
36
-
37
30
  try {
38
31
  const result = await submitPaywallFeedback(
39
32
  user?.uid ?? null,
@@ -81,13 +74,6 @@ export function useSettingsFeedbackSubmit(
81
74
 
82
75
  const submit = useCallback(
83
76
  async (data: SettingsFeedbackData) => {
84
- if (__DEV__) {
85
- console.log("[useSettingsFeedbackSubmit] Submitting:", {
86
- userId: user?.uid?.slice(0, 8),
87
- type: data.type,
88
- });
89
- }
90
-
91
77
  try {
92
78
  const result = await submitSettingsFeedback(
93
79
  user?.uid ?? null,
@@ -9,7 +9,7 @@ export interface ActivationHandlerConfig {
9
9
  userId: string,
10
10
  status: SubscriptionStatus
11
11
  ) => Promise<void> | void;
12
- onError?: (error: Error, context: string) => Promise<void> | void;
12
+ onError?: (error: Error, operation: string) => Promise<void> | void;
13
13
  }
14
14
 
15
15
  /**
@@ -85,15 +85,15 @@ async function notifyStatusChange(
85
85
  * Safe error handler - wraps error callbacks to prevent secondary failures
86
86
  */
87
87
  export async function safeHandleError(
88
- onError: ((error: Error, context: string) => Promise<void> | void) | undefined,
88
+ onError: ((error: Error, operation: string) => Promise<void> | void) | undefined,
89
89
  error: unknown,
90
- context: string
90
+ operation: string
91
91
  ): Promise<void> {
92
92
  if (!onError) return;
93
93
 
94
94
  try {
95
95
  const err = error instanceof Error ? error : new Error("Unknown error");
96
- await onError(err, context);
96
+ await onError(err, operation);
97
97
  } catch {
98
98
  // Ignore callback errors
99
99
  }
@@ -102,7 +102,7 @@ export async function safeHandleError(
102
102
  async function handleError(
103
103
  config: ActivationHandlerConfig,
104
104
  error: unknown,
105
- context: string
105
+ operation: string
106
106
  ): Promise<void> {
107
- await safeHandleError(config.onError, error, `ActivationHandler.${context}`);
107
+ await safeHandleError(config.onError, error, `ActivationHandler.${operation}`);
108
108
  }
@@ -32,30 +32,16 @@ export async function submitFeedback(
32
32
  const db = getFirestore();
33
33
 
34
34
  if (!db) {
35
- if (__DEV__) {
36
- console.warn("[FeedbackService] Firestore not available");
37
- }
38
35
  return { success: false, error: new Error("Firestore not available") };
39
36
  }
40
37
 
41
38
  if (!data.userId) {
42
- if (__DEV__) {
43
- console.warn("[FeedbackService] User ID is required for feedback");
44
- }
45
39
  return { success: false, error: new Error("User ID is required") };
46
40
  }
47
41
 
48
42
  try {
49
- if (__DEV__) {
50
- console.log("[FeedbackService] Submitting feedback:", {
51
- type: data.type,
52
- userId: data.userId?.slice(0, 8),
53
- });
54
- }
55
-
56
43
  const now = new Date().toISOString();
57
44
 
58
- // Store under users/{userId}/feedback
59
45
  const userDocRef = doc(db, "users", data.userId);
60
46
  const feedbackCollectionRef = collection(userDocRef, "feedback");
61
47
 
@@ -70,15 +56,8 @@ export async function submitFeedback(
70
56
  updatedAt: now,
71
57
  });
72
58
 
73
- if (__DEV__) {
74
- console.log("[FeedbackService] Feedback submitted successfully");
75
- }
76
-
77
59
  return { success: true };
78
60
  } catch (error) {
79
- if (__DEV__) {
80
- console.error("[FeedbackService] Submit error:", error);
81
- }
82
61
  return {
83
62
  success: false,
84
63
  error: error instanceof Error ? error : new Error("Unknown error"),
@@ -42,8 +42,7 @@ export class SubscriptionEventBus {
42
42
  this.listeners[event].forEach(callback => {
43
43
  try {
44
44
  callback(data);
45
- } catch (error) {
46
- if (__DEV__) console.error(`[SubscriptionEventBus] Error in listener for ${event}:`, error);
45
+ } catch {
47
46
  }
48
47
  });
49
48
  }
@@ -0,0 +1 @@
1
+ export * from "./layouts/ScreenLayout";
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Screen Layout Component
3
+ * Centralized layout with safe area handling and consistent styling.
4
+ */
5
+ import React from "react";
6
+ import { StyleSheet, View, ScrollView, type ViewStyle, type ColorValue } from "react-native";
7
+ import { useSafeAreaInsets, type Edge } from "react-native-safe-area-context";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
9
+
10
+ export interface ScreenLayoutProps {
11
+ children: React.ReactNode;
12
+ scrollable?: boolean;
13
+ edges?: Edge[];
14
+ backgroundColor?: ColorValue;
15
+ contentContainerStyle?: ViewStyle;
16
+ footer?: React.ReactNode;
17
+ }
18
+
19
+ export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
20
+ children,
21
+ scrollable = false,
22
+ edges = ["top", "bottom", "left", "right"],
23
+ backgroundColor,
24
+ contentContainerStyle,
25
+ footer,
26
+ }) => {
27
+ const tokens = useAppDesignTokens();
28
+ const insets = useSafeAreaInsets();
29
+
30
+ const containerStyle = [
31
+ styles.container,
32
+ {
33
+ backgroundColor: backgroundColor ?? tokens.colors.backgroundPrimary,
34
+ paddingTop: edges.includes("top") ? insets.top : 0,
35
+ paddingBottom: edges.includes("bottom") ? insets.bottom : 0,
36
+ paddingLeft: edges.includes("left") ? insets.left : 0,
37
+ paddingRight: edges.includes("right") ? insets.right : 0,
38
+ },
39
+ ];
40
+
41
+ const content = (
42
+ <>
43
+ <View style={[styles.flex, contentContainerStyle]}>
44
+ {children}
45
+ </View>
46
+ {footer}
47
+ </>
48
+ );
49
+
50
+ if (scrollable) {
51
+ return (
52
+ <ScrollView
53
+ style={containerStyle}
54
+ contentContainerStyle={[styles.scrollContent]}
55
+ showsVerticalScrollIndicator={false}
56
+ >
57
+ {content}
58
+ </ScrollView>
59
+ );
60
+ }
61
+
62
+ return (
63
+ <View style={containerStyle}>
64
+ {content}
65
+ </View>
66
+ );
67
+ };
68
+
69
+ const styles = StyleSheet.create({
70
+ container: {
71
+ flex: 1,
72
+ },
73
+ scrollContent: {
74
+ flexGrow: 1,
75
+ },
76
+ flex: {
77
+ flex: 1,
78
+ },
79
+ });
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Common Type Definitions
3
+ * Shared types used across multiple domains
4
+ */
5
+
6
+ /**
7
+ * Purchase result from any purchase operation
8
+ */
9
+ export interface PurchaseResult {
10
+ success: boolean;
11
+ productId?: string;
12
+ error?: Error;
13
+ }
14
+
15
+ /**
16
+ * Transaction result for repository operations
17
+ */
18
+ export interface TransactionResult<T = unknown> {
19
+ success: boolean;
20
+ data?: T;
21
+ error?: Error;
22
+ }
23
+
24
+ /**
25
+ * Subscription status information
26
+ */
27
+ export interface SubscriptionStatusInfo {
28
+ isActive: boolean;
29
+ isExpired: boolean;
30
+ isTrial: boolean;
31
+ willRenew: boolean;
32
+ expirationDate?: Date;
33
+ productId?: string;
34
+ }
35
+
36
+ /**
37
+ * Credits information
38
+ */
39
+ export interface CreditsInfo {
40
+ credits: number;
41
+ creditLimit: number;
42
+ isPremium: boolean;
43
+ status: string;
44
+ }
45
+
46
+ /**
47
+ * Transaction metadata for any transaction type
48
+ */
49
+ export interface TransactionMetadata {
50
+ productId: string;
51
+ amount: number;
52
+ currency?: string;
53
+ timestamp: Date;
54
+ type: 'purchase' | 'renewal' | 'restore' | 'credit_purchase';
55
+ }
56
+
57
+ /**
58
+ * Platform information
59
+ */
60
+ export type Platform = 'ios' | 'android';
61
+
62
+ /**
63
+ * Purchase source tracking
64
+ */
65
+ export type PurchaseSource = 'settings' | 'paywall' | 'upgrade_prompt' | 'auto-execution' | 'manual';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Base Error Class
3
+ * Common base error for all domain errors
4
+ */
5
+
6
+ export abstract class BaseError extends Error {
7
+ public readonly code: string;
8
+ public readonly cause?: Error;
9
+
10
+ constructor(message: string, code: string, cause?: Error) {
11
+ super(message);
12
+ this.name = this.constructor.name;
13
+ this.code = code;
14
+ this.cause = cause;
15
+ Object.setPrototypeOf(this, new.target.prototype);
16
+ }
17
+
18
+ toJSON() {
19
+ return {
20
+ name: this.name,
21
+ code: this.code,
22
+ message: this.message,
23
+ cause: this.cause?.message,
24
+ };
25
+ }
26
+ }
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Subscription Logger
3
3
  * Centralized logging utility for the subscription package.
4
- * All logs are dev-only and automatically stripped in production.
4
+ * All logs are disabled - no logging.
5
5
  */
6
6
 
7
7
  export type LogLevel = "debug" | "info" | "warn" | "error";
8
8
 
9
- export interface LogContext {
9
+ export interface LogMetadata {
10
10
  [key: string]: unknown;
11
11
  }
12
12
 
@@ -25,86 +25,55 @@ export type LogCategory = (typeof LOG_CATEGORY)[keyof typeof LOG_CATEGORY];
25
25
 
26
26
  /**
27
27
  * Development-only logger that automatically respects __DEV__ flag.
28
- * All methods are no-ops in production builds.
28
+ * All methods are no-ops - logging is disabled.
29
29
  */
30
30
  class SubscriptionLogger {
31
- private enabled: boolean = true;
32
- private categories: Set<LogCategory> = new Set();
33
-
34
31
  /** Enable or disable all logging */
35
- setEnabled(enabled: boolean): void {
36
- this.enabled = enabled;
32
+ setEnabled(_enabled: boolean): void {
37
33
  }
38
34
 
39
35
  /** Enable logging for specific categories only */
40
- setCategories(categories: LogCategory[]): void {
41
- this.categories = new Set(categories);
36
+ setCategories(_categories: LogCategory[]): void {
42
37
  }
43
38
 
44
39
  /** Clear category filter (log all categories) */
45
40
  clearCategories(): void {
46
- this.categories.clear();
47
- }
48
-
49
- private shouldLog(category?: LogCategory): boolean {
50
- if (typeof __DEV__ === "undefined" || !__DEV__) return false;
51
- if (!this.enabled) return false;
52
- if (this.categories.size > 0 && category && !this.categories.has(category)) return false;
53
- return true;
54
- }
55
-
56
- private formatMessage(category: LogCategory | string, message: string): string {
57
- return `[${category}] ${message}`;
58
41
  }
59
42
 
60
- debug(category: LogCategory, message: string, context?: LogContext): void {
61
- if (!this.shouldLog(category)) return;
62
- console.log(this.formatMessage(category, message), context ?? "");
43
+ debug(_category: LogCategory, _message: string, _metadata?: LogMetadata): void {
63
44
  }
64
45
 
65
- info(category: LogCategory, message: string, context?: LogContext): void {
66
- if (!this.shouldLog(category)) return;
67
- console.log(this.formatMessage(category, message), context ?? "");
46
+ info(_category: LogCategory, _message: string, _metadata?: LogMetadata): void {
68
47
  }
69
48
 
70
- warn(category: LogCategory, message: string, context?: LogContext): void {
71
- if (!this.shouldLog(category)) return;
72
- console.warn(this.formatMessage(category, message), context ?? "");
49
+ warn(_category: LogCategory, _message: string, _metadata?: LogMetadata): void {
73
50
  }
74
51
 
75
- error(category: LogCategory, message: string, error?: unknown, context?: LogContext): void {
76
- if (!this.shouldLog(category)) return;
77
- console.error(this.formatMessage(category, message), { error, ...context });
52
+ error(_category: LogCategory, _message: string, _error?: unknown, _metadata?: LogMetadata): void {
78
53
  }
79
54
 
80
55
  /** Log purchase flow events */
81
- purchase(message: string, context?: LogContext): void {
82
- this.debug(LOG_CATEGORY.PURCHASE, message, context);
56
+ purchase(_message: string, _metadata?: LogMetadata): void {
83
57
  }
84
58
 
85
59
  /** Log credits-related events */
86
- credits(message: string, context?: LogContext): void {
87
- this.debug(LOG_CATEGORY.CREDITS, message, context);
60
+ credits(_message: string, _metadata?: LogMetadata): void {
88
61
  }
89
62
 
90
63
  /** Log trial-related events */
91
- trial(message: string, context?: LogContext): void {
92
- this.debug(LOG_CATEGORY.TRIAL, message, context);
64
+ trial(_message: string, _metadata?: LogMetadata): void {
93
65
  }
94
66
 
95
67
  /** Log RevenueCat SDK events */
96
- revenueCat(message: string, context?: LogContext): void {
97
- this.debug(LOG_CATEGORY.REVENUECAT, message, context);
68
+ revenueCat(_message: string, _metadata?: LogMetadata): void {
98
69
  }
99
70
 
100
71
  /** Log feature gate events */
101
- featureGate(message: string, context?: LogContext): void {
102
- this.debug(LOG_CATEGORY.FEATURE_GATE, message, context);
72
+ featureGate(_message: string, _metadata?: LogMetadata): void {
103
73
  }
104
74
 
105
75
  /** Log sync operations */
106
- sync(message: string, context?: LogContext): void {
107
- this.debug(LOG_CATEGORY.SYNC, message, context);
76
+ sync(_message: string, _metadata?: LogMetadata): void {
108
77
  }
109
78
  }
110
79
 
@@ -114,6 +114,22 @@ export function tryCatchSync<T>(
114
114
  return failure(mappedError);
115
115
  }
116
116
  }
117
+ export function tryCatchSync<T>(
118
+ fn: () => T,
119
+ errorMapper?: (error: unknown) => Error
120
+ ): Result<T, Error> {
121
+ try {
122
+ const data = fn();
123
+ return success(data);
124
+ } catch {
125
+ const mappedError = errorMapper
126
+ ? errorMapper(error)
127
+ : error instanceof Error
128
+ ? error
129
+ : new Error(String(error));
130
+ return failure(mappedError);
131
+ }
132
+ }
117
133
 
118
134
  /** Combine multiple results into one */
119
135
  export function combine<T, E>(results: Result<T, E>[]): Result<T[], E> {
@@ -11,5 +11,5 @@ export interface SubscriptionConfig {
11
11
  entitlements?: string[];
12
12
  debugMode?: boolean;
13
13
  onStatusChanged?: (userId: string, status: SubscriptionStatus) => Promise<void> | void;
14
- onError?: (error: Error, context: string) => Promise<void> | void;
14
+ onError?: (error: Error, operation: string) => Promise<void> | void;
15
15
  }