@umituz/react-native-subscription 3.1.8 → 3.1.10

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-subscription",
3
- "version": "3.1.8",
3
+ "version": "3.1.10",
4
4
  "description": "Complete subscription management with RevenueCat, paywall UI, and credits system for React Native apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -2,7 +2,8 @@ import type { CreditsConfig } from "../core/Credits";
2
2
  import { getAppVersion, validatePlatform } from "../../../utils/appUtils";
3
3
 
4
4
  import type { InitializeCreditsMetadata, InitializationResult } from "../../subscription/application/SubscriptionInitializerTypes";
5
- import { runTransaction, type Transaction, type DocumentReference, type Firestore } from "@umituz/react-native-firebase";
5
+ import { runTransaction, type DocumentReference } from "firebase/firestore";
6
+ import type { Firestore } from "@umituz/react-native-firebase";
6
7
  import { getCreditDocumentOrDefault } from "./creditDocumentHelpers";
7
8
  import { calculateNewCredits, buildCreditsData } from "./creditOperationUtils";
8
9
  import { CreditLimitService } from "../domain/services/CreditLimitService";
@@ -39,7 +40,7 @@ export async function initializeCreditsTransaction(
39
40
  const platform = validatePlatform();
40
41
  const appVersion = getAppVersion();
41
42
 
42
- return runTransaction(async (transaction: Transaction) => {
43
+ return runTransaction(_db, async (transaction) => {
43
44
  const creditsDoc = await transaction.get(creditsRef);
44
45
 
45
46
  const existingData = getCreditDocumentOrDefault(creditsDoc, platform);
@@ -1,4 +1,5 @@
1
- import { runTransaction, serverTimestamp, type Transaction, type DocumentReference, type Firestore } from "@umituz/react-native-firebase";
1
+ import { runTransaction, serverTimestamp, type DocumentReference } from "firebase/firestore";
2
+ import type { Firestore } from "@umituz/react-native-firebase";
2
3
  import type { DeductCreditsResult } from "../core/Credits";
3
4
  import { CREDIT_ERROR_CODES, MAX_SINGLE_DEDUCTION } from "../core/CreditsConstants";
4
5
 
@@ -33,7 +34,7 @@ export async function deductCreditsOperation(
33
34
  try {
34
35
  if (__DEV__) console.log('[DeductCreditsCommand] >>> starting transaction', { userId, cost, creditsRefPath: creditsRef.path });
35
36
 
36
- const remaining = await runTransaction(async (tx: Transaction) => {
37
+ const remaining = await runTransaction(_db, async (tx) => {
37
38
  const docSnap = await tx.get(creditsRef);
38
39
 
39
40
  if (__DEV__) console.log('[DeductCreditsCommand] doc exists:', docSnap.exists());
@@ -66,7 +67,7 @@ export async function deductCreditsOperation(
66
67
 
67
68
  return {
68
69
  success: true,
69
- remainingCredits: remaining,
70
+ remainingCredits: remaining as number,
70
71
  error: null
71
72
  };
72
73
  } catch (e: unknown) {
@@ -1,4 +1,4 @@
1
- import { Timestamp } from "@umituz/react-native-firebase";
1
+ import { Timestamp } from "firebase/firestore";
2
2
  import type {
3
3
  PurchaseType,
4
4
  PurchaseMetadata,
@@ -1,4 +1,5 @@
1
- import { runTransaction, serverTimestamp, type Transaction, type DocumentReference, type Firestore } from "@umituz/react-native-firebase";
1
+ import { runTransaction, serverTimestamp, type DocumentReference } from "firebase/firestore";
2
+ import type { Firestore } from "@umituz/react-native-firebase";
2
3
  import type { DeductCreditsResult } from "../core/Credits";
3
4
  import { CREDIT_ERROR_CODES } from "../core/CreditsConstants";
4
5
 
@@ -31,7 +32,7 @@ export async function refundCreditsOperation(
31
32
  }
32
33
 
33
34
  try {
34
- const remaining = await runTransaction(async (tx: Transaction) => {
35
+ const remaining = await runTransaction(_db, async (tx) => {
35
36
  const docSnap = await tx.get(creditsRef);
36
37
 
37
38
  if (!docSnap.exists()) {
@@ -54,7 +55,7 @@ export async function refundCreditsOperation(
54
55
 
55
56
  return {
56
57
  success: true,
57
- remainingCredits: remaining,
58
+ remainingCredits: remaining as number,
58
59
  error: null
59
60
  };
60
61
  } catch (e: unknown) {
@@ -1,5 +1,6 @@
1
1
  import type { UserCreditsDocumentRead, FirestoreTimestamp } from "../core/UserCreditsDocument";
2
- import { serverTimestamp, type DocumentSnapshot } from "@umituz/react-native-firebase";
2
+ import { serverTimestamp } from "firebase/firestore";
3
+ import type { DocumentSnapshot } from "firebase/firestore";
3
4
  import { SUBSCRIPTION_STATUS, type Platform } from "../../subscription/core/SubscriptionConstants";
4
5
 
5
6
  export function getCreditDocumentOrDefault(
@@ -1,4 +1,5 @@
1
- import type { Firestore, DocumentReference } from "@umituz/react-native-firebase";
1
+ import type { DocumentReference } from "firebase/firestore";
2
+ import type { Firestore } from "@umituz/react-native-firebase";
2
3
  import { BaseRepository } from "@umituz/react-native-firebase";
3
4
  import type { CreditsConfig, CreditsResult, DeductCreditsResult } from "../core/Credits";
4
5
  import type { PurchaseSource } from "../core/UserCreditsDocument";
@@ -86,12 +87,12 @@ export class CreditsRepository extends BaseRepository {
86
87
 
87
88
  async syncExpiredStatus(userId: string): Promise<void> {
88
89
  const db = requireFirestore();
89
- await syncExpiredStatus(this.getRef(db, userId));
90
+ await syncExpiredStatus(db, this.getRef(db, userId));
90
91
  }
91
92
 
92
93
  async syncPremiumMetadata(userId: string, metadata: SubscriptionMetadata): Promise<void> {
93
94
  const db = requireFirestore();
94
- await syncPremiumMetadata(this.getRef(db, userId), metadata);
95
+ await syncPremiumMetadata(db, this.getRef(db, userId), metadata);
95
96
  }
96
97
 
97
98
  async ensurePremiumCreditsExist(
@@ -1,5 +1,4 @@
1
- import { getDoc } from "firebase/firestore";
2
- import type { DocumentReference } from "@umituz/react-native-firebase";
1
+ import { getDoc, type DocumentReference } from "firebase/firestore";
3
2
  import type { CreditsResult } from "../../core/Credits";
4
3
  import type { UserCreditsDocumentRead } from "../../core/UserCreditsDocument";
5
4
  import { mapCreditsDocumentToEntity } from "../../core/CreditsMapper";
@@ -1,4 +1,5 @@
1
- import type { Firestore, DocumentReference } from "@umituz/react-native-firebase";
1
+ import type { DocumentReference } from "firebase/firestore";
2
+ import type { Firestore } from "@umituz/react-native-firebase";
2
3
  import type { CreditsConfig, CreditsResult } from "../../core/Credits";
3
4
  import type { PurchaseSource } from "../../core/UserCreditsDocument";
4
5
  import { initializeCreditsTransaction } from "../../application/CreditsInitializer";
@@ -1,5 +1,5 @@
1
- import type { DocumentReference, Transaction } from "@umituz/react-native-firebase";
2
- import { runTransaction, serverTimestamp } from "@umituz/react-native-firebase";
1
+ import { runTransaction, serverTimestamp, type DocumentReference } from "firebase/firestore";
2
+ import type { Firestore } from "@umituz/react-native-firebase";
3
3
  import { getDoc, setDoc } from "firebase/firestore";
4
4
  import { SUBSCRIPTION_STATUS } from "../../../subscription/core/SubscriptionConstants";
5
5
  import { resolveSubscriptionStatus } from "../../../subscription/core/SubscriptionStatus";
@@ -11,8 +11,8 @@ import { getAppVersion, validatePlatform } from "../../../../utils/appUtils";
11
11
  // Fix: was getDoc+setDoc (non-atomic) — now uses runTransaction so concurrent
12
12
  // initializeCreditsTransaction and deductCreditsOperation no longer see stale
13
13
  // updateTime preconditions that produce failed-precondition errors.
14
- export async function syncExpiredStatus(ref: DocumentReference): Promise<void> {
15
- await runTransaction(async (tx: Transaction) => {
14
+ export async function syncExpiredStatus(db: Firestore, ref: DocumentReference): Promise<void> {
15
+ await runTransaction(db, async (tx) => {
16
16
  const doc = await tx.get(ref);
17
17
  if (!doc.exists()) return;
18
18
 
@@ -27,10 +27,11 @@ export async function syncExpiredStatus(ref: DocumentReference): Promise<void> {
27
27
 
28
28
  // Fix: was getDoc+setDoc (non-atomic) — now uses runTransaction.
29
29
  export async function syncPremiumMetadata(
30
+ db: Firestore,
30
31
  ref: DocumentReference,
31
32
  metadata: SubscriptionMetadata
32
33
  ): Promise<void> {
33
- await runTransaction(async (tx: Transaction) => {
34
+ await runTransaction(db, async (tx) => {
34
35
  const doc = await tx.get(ref);
35
36
  if (!doc.exists()) return;
36
37
 
@@ -1,10 +1,10 @@
1
- import { useEffect, useState, useCallback } from "react";
2
- import { onSnapshot } from "firebase/firestore";
3
- import type { UserCredits } from "../core/Credits";
1
+ import { useMemo } from "react";
2
+ import type { DocumentReference } from "firebase/firestore";
3
+ import type { UserCreditsDocumentRead } from "../core/UserCreditsDocument";
4
4
  import { getCreditsConfig } from "../infrastructure/CreditsRepositoryManager";
5
5
  import { mapCreditsDocumentToEntity } from "../core/CreditsMapper";
6
- import { requireFirestore, buildDocRef, type CollectionConfig } from "../../../shared/infrastructure/firestore/collectionUtils";
7
- import type { UserCreditsDocumentRead } from "../core/UserCreditsDocument";
6
+ import { requireFirestore, buildDocRef } from "../../../shared/infrastructure/firestore/collectionUtils";
7
+ import { useFirestoreDocumentRealTime } from "../../../shared/presentation/hooks/useFirestoreRealTime";
8
8
 
9
9
  /**
10
10
  * Real-time sync for credits using Firestore onSnapshot.
@@ -20,76 +20,29 @@ import type { UserCreditsDocumentRead } from "../core/UserCreditsDocument";
20
20
  * @returns Credits state and loading status
21
21
  */
22
22
  export function useCreditsRealTime(userId: string | null | undefined) {
23
- const [credits, setCredits] = useState<UserCredits | null>(null);
24
- const [isLoading, setIsLoading] = useState(true);
25
- const [error, setError] = useState<Error | null>(null);
26
-
27
- useEffect(() => {
28
- // Reset state when userId changes
29
- if (!userId) {
30
- setCredits(null);
31
- setIsLoading(false);
32
- setError(null);
33
- return;
34
- }
35
-
36
- setIsLoading(true);
37
- setError(null);
23
+ // Build document reference
24
+ const docRef = useMemo(() => {
25
+ if (!userId) return null;
38
26
 
39
- try {
40
- const db = requireFirestore();
41
- const config = getCreditsConfig();
42
-
43
- // Build doc ref using same logic as repository
44
- const collectionConfig: CollectionConfig = {
45
- collectionName: config.collectionName,
46
- useUserSubcollection: config.useUserSubcollection,
47
- };
48
- const docRef = buildDocRef(db, userId, "balance", collectionConfig);
49
-
50
- // Real-time listener
51
- const unsubscribe = onSnapshot(
52
- docRef,
53
- (snapshot) => {
54
- if (snapshot.exists()) {
55
- const entity = mapCreditsDocumentToEntity(snapshot.data() as UserCreditsDocumentRead);
56
- setCredits(entity);
57
- } else {
58
- setCredits(null);
59
- }
60
- setIsLoading(false);
61
- },
62
- (err) => {
63
- console.error("[useCreditsRealTime] Snapshot error:", err);
64
- setError(err as Error);
65
- setIsLoading(false);
66
- }
67
- );
68
-
69
- return () => {
70
- unsubscribe();
71
- };
72
- } catch (err) {
73
- const error = err instanceof Error ? err : new Error(String(err));
74
- console.error("[useCreditsRealTime] Setup error:", err);
75
- setError(error);
76
- setIsLoading(false);
77
- }
27
+ const db = requireFirestore();
28
+ const config = getCreditsConfig();
29
+ const ref = buildDocRef(db, userId, "balance", config);
30
+ return ref as DocumentReference<UserCreditsDocumentRead>;
78
31
  }, [userId]);
79
32
 
80
- const refetch = useCallback(() => {
81
- // Real-time sync doesn't need refetch, but keep for API compatibility
82
- // The snapshot listener will automatically update when data changes
83
- if (__DEV__) {
84
- console.warn("[useCreditsRealTime] Refetch called - not needed for real-time sync");
85
- }
86
- }, []);
33
+ // Use generic real-time sync hook
34
+ const { data, isLoading, error, refetch } = useFirestoreDocumentRealTime(
35
+ userId,
36
+ docRef,
37
+ mapCreditsDocumentToEntity,
38
+ "useCreditsRealTime"
39
+ );
87
40
 
88
41
  return {
89
- credits,
42
+ credits: data,
90
43
  isLoading,
91
44
  error,
92
- refetch, // No-op but kept for compatibility
45
+ refetch,
93
46
  };
94
47
  }
95
48
 
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Subscription Flow Initial State
3
+ *
4
+ * Default state for the subscription flow state machine.
5
+ */
6
+
7
+ import { SubscriptionFlowStatus, SyncStatus, type SubscriptionFlowState } from "./flowTypes";
8
+
9
+ /**
10
+ * Initial state for the subscription flow store.
11
+ * Represents a fresh install scenario.
12
+ */
13
+ export const initialFlowState: SubscriptionFlowState = {
14
+ status: SubscriptionFlowStatus.INITIALIZING,
15
+ syncStatus: SyncStatus.IDLE,
16
+ syncError: null,
17
+ isOnboardingComplete: false,
18
+ paywallShown: false,
19
+ showFeedback: false,
20
+ isAuthModalOpen: false,
21
+ isInitialized: false,
22
+ };
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Subscription Flow State Machine Types
3
+ *
4
+ * Type definitions for the subscription flow state machine.
5
+ * Separated from implementation for better maintainability.
6
+ */
7
+
8
+ /**
9
+ * States in the subscription flow.
10
+ * Represents the user's journey from first launch to premium access.
11
+ */
12
+ export enum SubscriptionFlowStatus {
13
+ /** App is initializing, determining next state */
14
+ INITIALIZING = "INITIALIZING",
15
+ /** User is seeing onboarding for the first time */
16
+ ONBOARDING = "ONBOARDING",
17
+ /** Checking if user has premium access */
18
+ CHECK_PREMIUM = "CHECK_PREMIUM",
19
+ /** Showing paywall after onboarding */
20
+ POST_ONBOARDING_PAYWALL = "POST_ONBOARDING_PAYWALL",
21
+ /** App is ready to use */
22
+ READY = "READY",
23
+ }
24
+
25
+ /**
26
+ * Sync status for subscription/credits data.
27
+ */
28
+ export enum SyncStatus {
29
+ /** No sync in progress */
30
+ IDLE = "IDLE",
31
+ /** Syncing data from RevenueCat/Firestore */
32
+ SYNCING = "SYNCING",
33
+ /** Sync completed successfully */
34
+ SUCCESS = "SUCCESS",
35
+ /** Sync failed with error */
36
+ ERROR = "ERROR",
37
+ }
38
+
39
+ /**
40
+ * Complete state shape for the subscription flow store.
41
+ */
42
+ export interface SubscriptionFlowState {
43
+ /** Current flow status */
44
+ status: SubscriptionFlowStatus;
45
+
46
+ /** Sync status for subscription/credits data */
47
+ syncStatus: SyncStatus;
48
+
49
+ /** Error message from last sync failure */
50
+ syncError: string | null;
51
+
52
+ /** Whether user has completed onboarding */
53
+ isOnboardingComplete: boolean;
54
+
55
+ /** Whether paywall has been shown at least once */
56
+ paywallShown: boolean;
57
+
58
+ /** Whether to show feedback screen */
59
+ showFeedback: boolean;
60
+
61
+ /** Whether auth modal is currently open */
62
+ isAuthModalOpen: boolean;
63
+
64
+ /** Whether store has been initialized */
65
+ isInitialized: boolean;
66
+ }
67
+
68
+ /**
69
+ * Actions available on the subscription flow store.
70
+ */
71
+ export interface SubscriptionFlowActions {
72
+ /** Mark onboarding as complete and transition to CHECK_PREMIUM */
73
+ completeOnboarding: () => void;
74
+
75
+ /** Show paywall and transition to POST_ONBOARDING_PAYWALL */
76
+ showPaywall: () => void;
77
+
78
+ /** Complete paywall interaction and transition to READY */
79
+ completePaywall: (purchased: boolean) => void;
80
+
81
+ /** Show feedback screen */
82
+ showFeedbackScreen: () => void;
83
+
84
+ /** Hide feedback screen */
85
+ hideFeedback: () => void;
86
+
87
+ /** Open/close auth modal */
88
+ setAuthModalOpen: (open: boolean) => void;
89
+
90
+ /** Update sync status */
91
+ setSyncStatus: (status: SyncStatus, error?: string | null) => void;
92
+
93
+ /** Set initialized flag (internal use) */
94
+ setInitialized: (initialized: boolean) => void;
95
+
96
+ /** Set flow status (internal use) */
97
+ setStatus: (status: SubscriptionFlowStatus) => void;
98
+
99
+ /** Reset flow to initial state */
100
+ resetFlow: () => void;
101
+ }
102
+
103
+ /**
104
+ * Combined store type with state and actions.
105
+ */
106
+ export type SubscriptionFlowStore = SubscriptionFlowState & SubscriptionFlowActions;
@@ -5,6 +5,9 @@ import {
5
5
  useRestorePurchase,
6
6
  } from '../infrastructure/hooks/useSubscriptionQueries';
7
7
  import { usePaywallVisibility } from './usePaywallVisibility';
8
+ import { createLogger } from '../../../shared/utils/logger';
9
+
10
+ const logger = createLogger('usePremiumActions');
8
11
 
9
12
  export interface PremiumActions {
10
13
  purchasePackage: (pkg: PurchasesPackage) => Promise<boolean>;
@@ -38,9 +41,7 @@ export function usePremiumActions(): PremiumActions {
38
41
  const result = await purchaseMutation.mutateAsync(pkg);
39
42
  return result.success;
40
43
  } catch (error) {
41
- if (__DEV__) {
42
- console.error('[usePremiumActions] Purchase failed:', error);
43
- }
44
+ logger.error('Purchase failed', error, { packageId: pkg.identifier });
44
45
  return false;
45
46
  }
46
47
  },
@@ -52,9 +53,7 @@ export function usePremiumActions(): PremiumActions {
52
53
  const result = await restoreMutation.mutateAsync();
53
54
  return result.success;
54
55
  } catch (error) {
55
- if (__DEV__) {
56
- console.error('[usePremiumActions] Restore failed:', error);
57
- }
56
+ logger.error('Restore failed', error);
58
57
  return false;
59
58
  }
60
59
  }, [restoreMutation]);
@@ -3,109 +3,35 @@
3
3
  *
4
4
  * Single source of truth for app flow state.
5
5
  * Clean state transitions without complex if/else logic.
6
+ *
7
+ * State transition rules:
8
+ * - INITIALIZING -> ONBOARDING (first launch)
9
+ * - INITIALIZING -> CHECK_PREMIUM (onboarding already done)
10
+ * - ONBOARDING -> CHECK_PREMIUM (onboarding completed)
11
+ * - CHECK_PREMIUM -> READY (user is premium)
12
+ * - CHECK_PREMIUM -> POST_ONBOARDING_PAYWALL (user not premium, paywall not shown)
13
+ * - CHECK_PREMIUM -> READY (user not premium but paywall already shown)
14
+ * - POST_ONBOARDING_PAYWALL -> READY (paywall closed)
15
+ * - READY -> READY (stays ready, shows overlays when needed)
6
16
  */
7
17
 
8
18
  import { createStore } from "@umituz/react-native-design-system/storage";
9
19
  import { subscriptionEventBus, FLOW_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
20
+ import {
21
+ SubscriptionFlowStatus,
22
+ SyncStatus,
23
+ type SubscriptionFlowState,
24
+ type SubscriptionFlowActions,
25
+ } from "./flowTypes";
26
+ import { initialFlowState } from "./flowInitialState";
10
27
 
11
- export enum SubscriptionFlowStatus {
12
- INITIALIZING = "INITIALIZING",
13
- ONBOARDING = "ONBOARDING",
14
- CHECK_PREMIUM = "CHECK_PREMIUM",
15
- POST_ONBOARDING_PAYWALL = "POST_ONBOARDING_PAYWALL",
16
- READY = "READY",
17
- }
18
-
19
- export enum SyncStatus {
20
- IDLE = "IDLE",
21
- SYNCING = "SYNCING",
22
- SUCCESS = "SUCCESS",
23
- ERROR = "ERROR",
24
- }
25
-
26
- export interface SubscriptionFlowState {
27
- // Flow state
28
- status: SubscriptionFlowStatus;
29
-
30
- // Sync state
31
- syncStatus: SyncStatus;
32
- syncError: string | null;
33
-
34
- // Onboarding state
35
- isOnboardingComplete: boolean;
36
-
37
- // Paywall state
38
- paywallShown: boolean;
39
-
40
- // Feedback state
41
- showFeedback: boolean;
42
-
43
- // Auth modal state
44
- isAuthModalOpen: boolean;
45
-
46
- // Initialization flag
47
- isInitialized: boolean;
48
- }
49
-
50
- export interface SubscriptionFlowActions {
51
- // Flow actions
52
- completeOnboarding: () => void;
53
- showPaywall: () => void;
54
- completePaywall: (purchased: boolean) => void;
55
- showFeedbackScreen: () => void;
56
- hideFeedback: () => void;
57
-
58
- // Auth actions
59
- setAuthModalOpen: (open: boolean) => void;
60
-
61
- // Sync actions
62
- setSyncStatus: (status: SyncStatus, error?: string | null) => void;
63
-
64
- // State setters (for internal use)
65
- setInitialized: (initialized: boolean) => void;
66
- setStatus: (status: SubscriptionFlowStatus) => void;
67
-
68
- // Reset
69
- resetFlow: () => void;
70
- }
71
-
72
- export type SubscriptionFlowStore = SubscriptionFlowState & SubscriptionFlowActions;
73
-
74
- const initialState: SubscriptionFlowState = {
75
- status: SubscriptionFlowStatus.INITIALIZING,
76
- syncStatus: SyncStatus.IDLE,
77
- syncError: null,
78
- isOnboardingComplete: false,
79
- paywallShown: false,
80
- showFeedback: false,
81
- isAuthModalOpen: false,
82
- isInitialized: false,
83
- };
84
-
85
- /**
86
- * State transition rules:
87
- *
88
- * INITIALIZING -> ONBOARDING (first launch)
89
- * INITIALIZING -> CHECK_PREMIUM (onboarding already done)
90
- *
91
- * ONBOARDING -> CHECK_PREMIUM (onboarding completed)
92
- *
93
- * CHECK_PREMIUM -> READY (user is premium)
94
- * CHECK_PREMIUM -> POST_ONBOARDING_PAYWALL (user not premium, paywall not shown)
95
- * CHECK_PREMIUM -> READY (user not premium but paywall already shown)
96
- *
97
- * POST_ONBOARDING_PAYWALL -> READY (paywall closed)
98
- *
99
- * READY -> READY (stays ready, shows overlays when needed)
100
- */
101
28
  export const useSubscriptionFlowStore = createStore<SubscriptionFlowState, SubscriptionFlowActions>({
102
29
  name: "subscription-flow-storage",
103
- initialState,
30
+ initialState: initialFlowState,
104
31
  persist: true,
105
32
  onRehydrate: (state) => {
106
33
  if (!state.isInitialized) {
107
34
  state.setInitialized(true);
108
-
109
35
  // First time: show onboarding
110
36
  state.setStatus(SubscriptionFlowStatus.INITIALIZING);
111
37
  } else if (state.isOnboardingComplete) {
@@ -176,3 +102,10 @@ export const useSubscriptionFlowStore = createStore<SubscriptionFlowState, Subsc
176
102
  },
177
103
  }),
178
104
  });
105
+
106
+ // Re-export types for convenience
107
+ export type { SubscriptionFlowState, SubscriptionFlowActions } from "./flowTypes";
108
+ export { SubscriptionFlowStatus, SyncStatus } from "./flowTypes";
109
+
110
+ // Re-export store type inferred from createStore
111
+ export type SubscriptionFlowStore = ReturnType<typeof useSubscriptionFlowStore>;