@sudobility/building_blocks 0.0.80 → 0.0.83

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.
@@ -45,6 +45,11 @@ export interface SudobilityAppWithFirebaseAuthProps extends Omit<SudobilityAppPr
45
45
  * Passed to ApiProvider. Defaults to false.
46
46
  */
47
47
  testMode?: boolean;
48
+ /**
49
+ * Base URL for API requests (optional).
50
+ * Passed to ApiProvider. Defaults to VITE_API_URL env var.
51
+ */
52
+ baseUrl?: string;
48
53
  }
49
54
  /**
50
55
  * SudobilityAppWithFirebaseAuth - App wrapper with Firebase authentication
@@ -81,5 +86,5 @@ export interface SudobilityAppWithFirebaseAuthProps extends Omit<SudobilityAppPr
81
86
  * }
82
87
  * ```
83
88
  */
84
- export declare function SudobilityAppWithFirebaseAuth({ AuthProviderWrapper: AuthProviderWrapperProp, authProviders, enableAnonymousAuth, ApiProvider: ApiProviderProp, AppProviders, testMode, ...baseProps }: SudobilityAppWithFirebaseAuthProps): import("react/jsx-runtime").JSX.Element;
89
+ export declare function SudobilityAppWithFirebaseAuth({ AuthProviderWrapper: AuthProviderWrapperProp, authProviders, enableAnonymousAuth, ApiProvider: ApiProviderProp, AppProviders, testMode, baseUrl, ...baseProps }: SudobilityAppWithFirebaseAuthProps): import("react/jsx-runtime").JSX.Element;
85
90
  export default SudobilityAppWithFirebaseAuth;
@@ -21,11 +21,17 @@ export interface SudobilityAppWithFirebaseAuthAndEntitiesProps extends Omit<Sudo
21
21
  */
22
22
  entityApiUrl?: string;
23
23
  /**
24
- * RevenueCat API key for subscriptions (optional).
24
+ * RevenueCat API key for production subscriptions (optional).
25
25
  * If not provided, reads from VITE_REVENUECAT_API_KEY env var.
26
26
  * If neither is available, subscription features are disabled.
27
27
  */
28
28
  revenueCatApiKey?: string;
29
+ /**
30
+ * RevenueCat API key for sandbox/test subscriptions (optional).
31
+ * Used when testMode is true.
32
+ * If not provided, reads from VITE_REVENUECAT_API_KEY_SANDBOX env var.
33
+ */
34
+ revenueCatApiKeySandbox?: string;
29
35
  /**
30
36
  * Custom AuthAwareEntityProvider component (optional).
31
37
  * Defaults to built-in provider if entityApiUrl is provided.
@@ -90,5 +96,5 @@ export interface SudobilityAppWithFirebaseAuthAndEntitiesProps extends Omit<Sudo
90
96
  * ```
91
97
  */
92
98
  export declare function SudobilityAppWithFirebaseAuthAndEntities({ apiUrl, entityApiUrl, // deprecated, use apiUrl
93
- revenueCatApiKey, AuthAwareEntityProvider: AuthAwareEntityProviderProp, EntityAwareSubscriptionProvider: EntityAwareSubscriptionProviderProp, AppProviders, ...baseProps }: SudobilityAppWithFirebaseAuthAndEntitiesProps): import("react/jsx-runtime").JSX.Element;
99
+ revenueCatApiKey, revenueCatApiKeySandbox, AuthAwareEntityProvider: AuthAwareEntityProviderProp, EntityAwareSubscriptionProvider: EntityAwareSubscriptionProviderProp, AppProviders, testMode, ...baseProps }: SudobilityAppWithFirebaseAuthAndEntitiesProps): import("react/jsx-runtime").JSX.Element;
94
100
  export default SudobilityAppWithFirebaseAuthAndEntities;
package/dist/firebase.js CHANGED
@@ -202,6 +202,7 @@ function SudobilityAppWithFirebaseAuth({
202
202
  ApiProvider: ApiProviderProp,
203
203
  AppProviders,
204
204
  testMode = false,
205
+ baseUrl,
205
206
  ...baseProps
206
207
  }) {
207
208
  const CombinedProviders = ({
@@ -215,7 +216,7 @@ function SudobilityAppWithFirebaseAuth({
215
216
  else if (ApiProviderProp) {
216
217
  content = /* @__PURE__ */ jsx(ApiProviderProp, { children: content });
217
218
  } else {
218
- content = /* @__PURE__ */ jsx(ApiProvider, { testMode, children: content });
219
+ content = /* @__PURE__ */ jsx(ApiProvider, { baseUrl, testMode, children: content });
219
220
  }
220
221
  if (AuthProviderWrapperProp) {
221
222
  return /* @__PURE__ */ jsx(AuthProviderWrapperProp, { children: content });
@@ -369,9 +370,11 @@ function SudobilityAppWithFirebaseAuthAndEntities({
369
370
  entityApiUrl,
370
371
  // deprecated, use apiUrl
371
372
  revenueCatApiKey,
373
+ revenueCatApiKeySandbox,
372
374
  AuthAwareEntityProvider: AuthAwareEntityProviderProp,
373
375
  EntityAwareSubscriptionProvider: EntityAwareSubscriptionProviderProp,
374
376
  AppProviders,
377
+ testMode = false,
375
378
  ...baseProps
376
379
  }) {
377
380
  const baseApiUrl = apiUrl || void 0 || "";
@@ -380,7 +383,9 @@ function SudobilityAppWithFirebaseAuthAndEntities({
380
383
  () => entityUrl ? getOrCreateEntityClient(entityUrl) : null,
381
384
  [entityUrl]
382
385
  );
383
- const rcApiKey = revenueCatApiKey || void 0;
386
+ const rcApiKeyProd = revenueCatApiKey || void 0 || "";
387
+ const rcApiKeySandbox = revenueCatApiKeySandbox || void 0 || "";
388
+ const rcApiKey = testMode ? rcApiKeySandbox : rcApiKeyProd;
384
389
  const EntityProviders = ({
385
390
  children
386
391
  }) => {
@@ -409,6 +414,8 @@ function SudobilityAppWithFirebaseAuthAndEntities({
409
414
  SudobilityAppWithFirebaseAuth,
410
415
  {
411
416
  ...baseProps,
417
+ baseUrl: baseApiUrl,
418
+ testMode,
412
419
  AppProviders: EntityProviders
413
420
  }
414
421
  );
@@ -1 +1 @@
1
- {"version":3,"file":"firebase.js","sources":["../src/components/api/ApiContext.tsx","../src/components/app/SudobilityAppWithFirebaseAuth.tsx","../src/components/subscription/LazySubscriptionProvider.tsx","../src/components/subscription/SubscriptionProviderWrapper.tsx","../src/components/app/SudobilityAppWithFirebaseAuthAndEntities.tsx"],"sourcesContent":["/**\n * API Context and Provider\n *\n * Provides network client, auth token, and API configuration to the app.\n */\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n useMemo,\n useCallback,\n type ReactNode,\n} from 'react';\nimport { networkClient, getInfoService } from '@sudobility/di';\nimport { InfoType } from '@sudobility/types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport { getFirebaseAuth } from '@sudobility/auth_lib';\n\n/**\n * API context value provided to consumers\n */\nexport interface ApiContextValue {\n /** Network client for making API requests */\n networkClient: NetworkClient;\n /** Base URL for API requests */\n baseUrl: string;\n /** Current user ID (Firebase UID) */\n userId: string | null;\n /** Firebase ID token for authenticated requests */\n token: string | null;\n /** Whether API is ready (user authenticated and token available) */\n isReady: boolean;\n /** Whether auth/token is still loading */\n isLoading: boolean;\n /** Force refresh the ID token */\n refreshToken: () => Promise<string | null>;\n /** Whether running in test/sandbox mode */\n testMode: boolean;\n}\n\nconst ApiContext = createContext<ApiContextValue | null>(null);\n\n/**\n * Hook to access API context\n * @throws Error if used outside of ApiProvider\n */\nexport function useApi(): ApiContextValue {\n const context = useContext(ApiContext);\n if (!context) {\n throw new Error('useApi must be used within an ApiProvider');\n }\n return context;\n}\n\n/**\n * Hook to safely access API context (returns null if not available)\n */\nexport function useApiSafe(): ApiContextValue | null {\n return useContext(ApiContext);\n}\n\ninterface ApiProviderProps {\n children: ReactNode;\n /**\n * Base URL for API requests (optional).\n * Defaults to VITE_API_URL env var.\n */\n baseUrl?: string;\n /**\n * Whether running in test/sandbox mode (optional).\n * Defaults to false.\n */\n testMode?: boolean;\n}\n\n/**\n * API Provider\n *\n * Manages Firebase ID token and provides API configuration.\n * Uses VITE_API_URL env var for baseUrl by default.\n */\nexport function ApiProvider({\n children,\n baseUrl: baseUrlProp,\n testMode = false,\n}: ApiProviderProps) {\n const { user, loading: authLoading } = useAuthStatus();\n const [token, setToken] = useState<string | null>(null);\n const [tokenLoading, setTokenLoading] = useState(false);\n const auth = getFirebaseAuth();\n\n const baseUrl = baseUrlProp || import.meta.env.VITE_API_URL || '';\n const userId = user?.uid ?? null;\n\n // Fetch token when user changes\n useEffect(() => {\n let mounted = true;\n\n const fetchToken = async () => {\n if (!userId) {\n setToken(null);\n setTokenLoading(false);\n return;\n }\n\n setTokenLoading(true);\n try {\n const currentUser = auth?.currentUser;\n if (!currentUser) {\n setToken(null);\n return;\n }\n const idToken = await currentUser.getIdToken();\n if (mounted) {\n setToken(idToken);\n }\n } catch {\n try {\n getInfoService().show(\n 'Authentication Error',\n 'Failed to get ID token',\n InfoType.ERROR,\n 5000\n );\n } catch {\n console.error('[ApiProvider] Failed to get ID token');\n }\n if (mounted) {\n setToken(null);\n }\n } finally {\n if (mounted) {\n setTokenLoading(false);\n }\n }\n };\n\n fetchToken();\n\n return () => {\n mounted = false;\n };\n }, [userId, auth]);\n\n // Refresh token function for when token expires\n const refreshToken = useCallback(async (): Promise<string | null> => {\n const currentUser = auth?.currentUser;\n if (!currentUser) return null;\n try {\n const newToken = await currentUser.getIdToken(true); // Force refresh\n setToken(newToken);\n return newToken;\n } catch {\n try {\n getInfoService().show(\n 'Authentication Error',\n 'Failed to refresh ID token',\n InfoType.ERROR,\n 5000\n );\n } catch {\n console.error('[ApiProvider] Failed to refresh ID token');\n }\n setToken(null);\n return null;\n }\n }, [auth]);\n\n const value = useMemo<ApiContextValue>(\n () => ({\n networkClient,\n baseUrl,\n userId,\n token,\n isReady: !!userId && !!token,\n isLoading: authLoading || tokenLoading,\n refreshToken,\n testMode,\n }),\n [baseUrl, userId, token, authLoading, tokenLoading, refreshToken, testMode]\n );\n\n return <ApiContext.Provider value={value}>{children}</ApiContext.Provider>;\n}\n\nexport { ApiContext };\n","/**\n * SudobilityAppWithFirebaseAuth - App wrapper with Firebase authentication\n *\n * Extends SudobilityApp with:\n * - Firebase AuthProviderWrapper for authentication (built-in default)\n * - ApiProvider for network/token management (built-in default)\n */\nimport { ComponentType, ReactNode, useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { SudobilityApp, SudobilityAppProps } from './SudobilityApp';\nimport { AuthProvider } from '@sudobility/auth-components';\nimport { getFirebaseAuth, getFirebaseErrorMessage } from '@sudobility/auth_lib';\nimport { ApiProvider } from '../api';\n\nexport interface SudobilityAppWithFirebaseAuthProps extends Omit<\n SudobilityAppProps,\n 'AppProviders'\n> {\n /**\n * Custom Firebase auth provider wrapper component (optional).\n * Defaults to built-in AuthProviderWrapper that uses getFirebaseAuth().\n */\n AuthProviderWrapper?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Auth providers to enable (optional).\n * Defaults to [\"google\", \"email\"].\n */\n authProviders?: ('google' | 'email' | 'apple')[];\n\n /**\n * Whether to enable anonymous auth (optional).\n * Defaults to false.\n */\n enableAnonymousAuth?: boolean;\n\n /**\n * Custom ApiProvider component (optional).\n * Defaults to built-in ApiProvider that manages Firebase ID token.\n * Set to false to disable the built-in ApiProvider.\n */\n ApiProvider?: ComponentType<{ children: ReactNode }> | false;\n\n /**\n * Additional providers to wrap around the router content.\n * These are rendered inside ApiProvider but outside BrowserRouter.\n */\n AppProviders?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Whether running in test/sandbox mode (optional).\n * Passed to ApiProvider. Defaults to false.\n */\n testMode?: boolean;\n}\n\n/**\n * Default auth texts - minimal fallback when translations aren't available\n */\nfunction createDefaultAuthTexts(t: (key: string) => string) {\n return {\n signInTitle: t('auth.signInTitle') || 'Sign In',\n signInWithEmail: t('auth.signInWithEmail') || 'Sign in with email',\n createAccount: t('auth.createAccount') || 'Create Account',\n resetPassword: t('auth.resetPassword') || 'Reset Password',\n signIn: t('auth.signIn') || 'Sign In',\n signUp: t('auth.signUp') || 'Sign Up',\n logout: t('auth.logout') || 'Log Out',\n login: t('auth.login') || 'Log In',\n continueWithGoogle: t('auth.continueWithGoogle') || 'Continue with Google',\n continueWithApple: t('auth.continueWithApple') || 'Continue with Apple',\n continueWithEmail: t('auth.continueWithEmail') || 'Continue with Email',\n sendResetLink: t('auth.sendResetLink') || 'Send Reset Link',\n backToSignIn: t('auth.backToSignIn') || 'Back to Sign In',\n close: t('auth.close') || 'Close',\n email: t('auth.email') || 'Email',\n password: t('auth.password') || 'Password',\n confirmPassword: t('auth.confirmPassword') || 'Confirm Password',\n displayName: t('auth.displayName') || 'Display Name',\n emailPlaceholder: t('auth.emailPlaceholder') || 'Enter your email',\n passwordPlaceholder: t('auth.passwordPlaceholder') || 'Enter your password',\n confirmPasswordPlaceholder:\n t('auth.confirmPasswordPlaceholder') || 'Confirm your password',\n displayNamePlaceholder:\n t('auth.displayNamePlaceholder') || 'Enter your name',\n forgotPassword: t('auth.forgotPassword') || 'Forgot Password?',\n noAccount: t('auth.noAccount') || \"Don't have an account?\",\n haveAccount: t('auth.haveAccount') || 'Already have an account?',\n or: t('auth.or') || 'or',\n resetEmailSent: t('auth.resetEmailSent') || 'Email Sent',\n resetEmailSentDesc:\n t('auth.resetEmailSentDesc') ||\n 'Check your inbox for the password reset link.',\n passwordMismatch: t('auth.passwordMismatch') || \"Passwords don't match\",\n passwordTooShort:\n t('auth.passwordTooShort') || 'Password must be at least 6 characters',\n loading: t('auth.loading') || 'Loading...',\n };\n}\n\n/**\n * Default auth error texts\n */\nfunction createDefaultAuthErrorTexts() {\n return {\n 'auth/user-not-found': 'No account found with this email',\n 'auth/wrong-password': 'Incorrect password',\n 'auth/invalid-email': 'Please enter a valid email address',\n 'auth/invalid-credential': 'Invalid credentials',\n 'auth/email-already-in-use': 'An account already exists with this email',\n 'auth/weak-password': 'Password should be at least 6 characters',\n 'auth/too-many-requests': 'Too many attempts. Please try again later.',\n 'auth/network-request-failed':\n 'Network error. Please check your connection.',\n 'auth/popup-closed-by-user': 'Sign in was cancelled',\n 'auth/popup-blocked':\n 'Pop-up was blocked. Please allow pop-ups and try again.',\n 'auth/account-exists-with-different-credential':\n 'An account already exists with this email using a different sign-in method',\n 'auth/operation-not-allowed': 'This sign-in method is not enabled',\n default: 'An error occurred. Please try again.',\n };\n}\n\n/**\n * Default AuthProviderWrapper using Firebase\n */\nfunction DefaultAuthProviderWrapper({\n children,\n providers,\n enableAnonymous,\n}: {\n children: ReactNode;\n providers: ('google' | 'email' | 'apple')[];\n enableAnonymous: boolean;\n}) {\n const { t } = useTranslation();\n\n const texts = useMemo(() => createDefaultAuthTexts(t), [t]);\n const errorTexts = useMemo(() => createDefaultAuthErrorTexts(), []);\n\n const auth = getFirebaseAuth();\n\n // If Firebase is not configured, render children without auth\n if (!auth) {\n console.warn(\n '[SudobilityAppWithFirebaseAuth] No auth instance - Firebase not configured'\n );\n return <>{children}</>;\n }\n\n return (\n <AuthProvider\n firebaseConfig={{ type: 'instance', auth: auth }}\n providerConfig={{\n providers,\n enableAnonymous,\n }}\n texts={texts}\n errorTexts={errorTexts}\n resolveErrorMessage={getFirebaseErrorMessage}\n >\n {children}\n </AuthProvider>\n );\n}\n\n/**\n * SudobilityAppWithFirebaseAuth - App wrapper with Firebase authentication\n *\n * @example\n * ```tsx\n * // Minimal usage - uses default auth with Google and Email\n * import { SudobilityAppWithFirebaseAuth } from '@sudobility/building_blocks';\n * import i18n from './i18n';\n *\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuth i18n={i18n}>\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuth>\n * );\n * }\n *\n * // With custom auth providers\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuth\n * i18n={i18n}\n * authProviders={[\"google\", \"apple\", \"email\"]}\n * AppProviders={ApiProvider}\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuth>\n * );\n * }\n * ```\n */\nexport function SudobilityAppWithFirebaseAuth({\n AuthProviderWrapper: AuthProviderWrapperProp,\n authProviders = ['google', 'email'],\n enableAnonymousAuth = false,\n ApiProvider: ApiProviderProp,\n AppProviders,\n testMode = false,\n ...baseProps\n}: SudobilityAppWithFirebaseAuthProps) {\n // Create a combined providers component that includes auth and api\n const CombinedProviders: ComponentType<{ children: ReactNode }> = ({\n children,\n }) => {\n let content = children;\n\n // Wrap with AppProviders if provided\n if (AppProviders) {\n content = <AppProviders>{content}</AppProviders>;\n }\n\n // Wrap with ApiProvider (custom, default, or disabled)\n if (ApiProviderProp === false) {\n // Explicitly disabled\n } else if (ApiProviderProp) {\n content = <ApiProviderProp>{content}</ApiProviderProp>;\n } else {\n // Default ApiProvider\n content = <ApiProvider testMode={testMode}>{content}</ApiProvider>;\n }\n\n // Wrap with AuthProviderWrapper (custom or default)\n if (AuthProviderWrapperProp) {\n return <AuthProviderWrapperProp>{content}</AuthProviderWrapperProp>;\n }\n\n return (\n <DefaultAuthProviderWrapper\n providers={authProviders}\n enableAnonymous={enableAnonymousAuth}\n >\n {content}\n </DefaultAuthProviderWrapper>\n );\n };\n\n return <SudobilityApp {...baseProps} AppProviders={CombinedProviders} />;\n}\n\nexport default SudobilityAppWithFirebaseAuth;\n","/**\n * Lazy Subscription Provider\n *\n * Defers loading of RevenueCat SDK (~600KB) until user is authenticated.\n * For unauthenticated users, provides a stub context.\n */\n\nimport { type ReactNode, Suspense, lazy, useMemo } from 'react';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport {\n SafeSubscriptionContext,\n STUB_SUBSCRIPTION_VALUE,\n} from './SafeSubscriptionContext';\n\n// Lazy load the actual subscription provider\nconst SubscriptionProviderWrapper = lazy(\n () => import('./SubscriptionProviderWrapper')\n);\n\nfunction StubSubscriptionProvider({ children }: { children: ReactNode }) {\n return (\n <SafeSubscriptionContext.Provider value={STUB_SUBSCRIPTION_VALUE}>\n {children}\n </SafeSubscriptionContext.Provider>\n );\n}\n\ninterface LazySubscriptionProviderProps {\n children: ReactNode;\n /** Entity ID to use as RevenueCat subscriber */\n entityId?: string;\n /** RevenueCat API key */\n apiKey: string;\n}\n\n/**\n * Lazy wrapper for SubscriptionProvider that only loads RevenueCat SDK\n * when the user is authenticated. This saves ~600KB on initial load.\n * For unauthenticated users, provides a stub context so hooks don't throw.\n */\nexport function LazySubscriptionProvider({\n children,\n entityId,\n apiKey,\n}: LazySubscriptionProviderProps) {\n const { user } = useAuthStatus();\n\n const isAuthenticated = useMemo(() => {\n return !!user && !user.isAnonymous;\n }, [user]);\n\n if (!isAuthenticated) {\n return <StubSubscriptionProvider>{children}</StubSubscriptionProvider>;\n }\n\n return (\n <Suspense\n fallback={<StubSubscriptionProvider>{children}</StubSubscriptionProvider>}\n >\n <SubscriptionProviderWrapper entityId={entityId} apiKey={apiKey}>\n {children}\n </SubscriptionProviderWrapper>\n </Suspense>\n );\n}\n\nexport default LazySubscriptionProvider;\n","/**\n * Subscription Provider Wrapper\n *\n * Integrates subscription-components with auth and sets up RevenueCat user.\n */\n\nimport { type ReactNode, useEffect, useRef } from 'react';\nimport {\n SubscriptionProvider,\n useSubscriptionContext,\n} from '@sudobility/subscription-components';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport { getInfoService } from '@sudobility/di';\nimport { InfoType } from '@sudobility/types';\nimport {\n setRevenueCatUser,\n clearRevenueCatUser,\n refreshSubscription,\n} from '@sudobility/subscription_lib';\nimport { SafeSubscriptionContext } from './SafeSubscriptionContext';\n\ninterface SubscriptionProviderWrapperProps {\n children: ReactNode;\n entityId?: string;\n apiKey: string;\n}\n\nconst handleSubscriptionError = (error: Error) => {\n try {\n getInfoService().show(\n 'Subscription Error',\n error.message,\n InfoType.ERROR,\n 5000\n );\n } catch {\n // InfoService not available - log to console\n console.error('[Subscription]', error.message);\n }\n};\n\n/**\n * Bridge that provides context and configures RevenueCat user.\n */\nfunction SubscriptionBridge({\n children,\n entityId,\n}: {\n children: ReactNode;\n entityId?: string;\n}) {\n const { user } = useAuthStatus();\n const context = useSubscriptionContext();\n const entityIdRef = useRef<string | null>(null);\n\n useEffect(() => {\n const shouldSetUser = user && !user.isAnonymous && entityId;\n\n if (shouldSetUser && entityId !== entityIdRef.current) {\n entityIdRef.current = entityId;\n // Set user for both subscription-components and subscription_lib\n context.initialize(entityId, user.email || undefined);\n setRevenueCatUser(entityId, user.email || undefined).then(() => {\n // Refresh subscription_lib data after user is set\n refreshSubscription();\n });\n } else if (!shouldSetUser && entityIdRef.current) {\n entityIdRef.current = null;\n clearRevenueCatUser();\n }\n }, [user, entityId, context]);\n\n return (\n <SafeSubscriptionContext.Provider value={context}>\n {children}\n </SafeSubscriptionContext.Provider>\n );\n}\n\nexport function SubscriptionProviderWrapper({\n children,\n entityId,\n apiKey,\n}: SubscriptionProviderWrapperProps) {\n const { user } = useAuthStatus();\n\n return (\n <SubscriptionProvider\n apiKey={apiKey}\n userEmail={user?.email || undefined}\n onError={handleSubscriptionError}\n >\n <SubscriptionBridge entityId={entityId}>{children}</SubscriptionBridge>\n </SubscriptionProvider>\n );\n}\n\nexport default SubscriptionProviderWrapper;\n","/**\n * SudobilityAppWithFirebaseAuthAndEntities - App wrapper with Firebase auth and entity support\n *\n * Extends SudobilityAppWithFirebaseAuth with:\n * - AuthAwareEntityProvider that connects entity context to auth state (built-in default)\n * - EntityAwareSubscriptionProvider that connects subscription to entity (built-in default)\n */\nimport { ComponentType, ReactNode, useMemo } from 'react';\nimport {\n SudobilityAppWithFirebaseAuth,\n SudobilityAppWithFirebaseAuthProps,\n} from './SudobilityAppWithFirebaseAuth';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport {\n CurrentEntityProvider,\n EntityClient,\n useCurrentEntity,\n} from '@sudobility/entity_client';\nimport { getFirebaseAuth } from '@sudobility/auth_lib';\nimport { LazySubscriptionProvider } from '../subscription';\n\nexport interface SudobilityAppWithFirebaseAuthAndEntitiesProps extends Omit<\n SudobilityAppWithFirebaseAuthProps,\n 'AppProviders'\n> {\n /**\n * Base URL for the API (optional).\n * Defaults to VITE_API_URL env var.\n * Used for both ApiProvider and EntityClient.\n *\n * @example \"https://api.myapp.com\"\n */\n apiUrl?: string;\n\n /**\n * @deprecated Use apiUrl instead. Will be removed in future version.\n */\n entityApiUrl?: string;\n\n /**\n * RevenueCat API key for subscriptions (optional).\n * If not provided, reads from VITE_REVENUECAT_API_KEY env var.\n * If neither is available, subscription features are disabled.\n */\n revenueCatApiKey?: string;\n\n /**\n * Custom AuthAwareEntityProvider component (optional).\n * Defaults to built-in provider if entityApiUrl is provided.\n */\n AuthAwareEntityProvider?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Custom EntityAwareSubscriptionProvider component (optional).\n * Defaults to built-in provider that uses LazySubscriptionProvider.\n * Set to false to disable subscription features entirely.\n */\n EntityAwareSubscriptionProvider?:\n | ComponentType<{ children: ReactNode }>\n | false;\n\n /**\n * Additional providers to wrap around the router content.\n * These are rendered inside EntityAwareSubscriptionProvider but outside BrowserRouter.\n * Use this for app-specific providers like ApiProvider.\n */\n AppProviders?: ComponentType<{ children: ReactNode }>;\n}\n\n/**\n * Get Firebase auth token for API requests\n */\nasync function getAuthToken(): Promise<string | null> {\n const auth = getFirebaseAuth();\n const currentUser = auth?.currentUser;\n if (!currentUser) {\n return null;\n }\n try {\n return await currentUser.getIdToken();\n } catch {\n return null;\n }\n}\n\n/**\n * Create a default entity client for the given API URL\n */\nfunction createEntityClient(baseUrl: string): EntityClient {\n return new EntityClient({\n baseUrl,\n getAuthToken,\n });\n}\n\n// Cache for entity clients by URL\nconst entityClientCache = new Map<string, EntityClient>();\n\nfunction getOrCreateEntityClient(baseUrl: string): EntityClient {\n let client = entityClientCache.get(baseUrl);\n if (!client) {\n client = createEntityClient(baseUrl);\n entityClientCache.set(baseUrl, client);\n }\n return client;\n}\n\n/**\n * Default AuthAwareEntityProvider using CurrentEntityProvider\n */\nfunction DefaultAuthAwareEntityProvider({\n children,\n entityClient,\n}: {\n children: ReactNode;\n entityClient: EntityClient;\n}) {\n const { user } = useAuthStatus();\n const authUser = useMemo(\n () => (user ? { uid: user.uid, email: user.email } : null),\n [user]\n );\n\n return (\n <CurrentEntityProvider client={entityClient} user={authUser}>\n {children}\n </CurrentEntityProvider>\n );\n}\n\n/**\n * Default EntityAwareSubscriptionProvider using LazySubscriptionProvider\n */\nfunction DefaultEntityAwareSubscriptionProvider({\n children,\n apiKey,\n}: {\n children: ReactNode;\n apiKey: string;\n}) {\n const { currentEntityId } = useCurrentEntity();\n return (\n <LazySubscriptionProvider\n entityId={currentEntityId ?? undefined}\n apiKey={apiKey}\n >\n {children}\n </LazySubscriptionProvider>\n );\n}\n\n/**\n * SudobilityAppWithFirebaseAuthAndEntities - Full-featured app wrapper\n *\n * @example\n * ```tsx\n * // Minimal usage with entityApiUrl - uses all defaults\n * import { SudobilityAppWithFirebaseAuthAndEntities } from '@sudobility/building_blocks';\n * import i18n from './i18n';\n *\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuthAndEntities\n * i18n={i18n}\n * entityApiUrl=\"https://api.myapp.com/api/v1\"\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuthAndEntities>\n * );\n * }\n *\n * // With custom providers\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuthAndEntities\n * i18n={i18n}\n * AuthAwareEntityProvider={MyEntityProvider}\n * EntityAwareSubscriptionProvider={MySubscriptionProvider}\n * AppProviders={ApiProvider}\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuthAndEntities>\n * );\n * }\n * ```\n */\nexport function SudobilityAppWithFirebaseAuthAndEntities({\n apiUrl,\n entityApiUrl, // deprecated, use apiUrl\n revenueCatApiKey,\n AuthAwareEntityProvider: AuthAwareEntityProviderProp,\n EntityAwareSubscriptionProvider: EntityAwareSubscriptionProviderProp,\n AppProviders,\n ...baseProps\n}: SudobilityAppWithFirebaseAuthAndEntitiesProps) {\n // Get API URL from prop or env var\n // Support deprecated entityApiUrl for backwards compatibility\n const baseApiUrl = apiUrl || import.meta.env.VITE_API_URL || '';\n const entityUrl = entityApiUrl || (baseApiUrl ? `${baseApiUrl}/api/v1` : '');\n\n // Get or create entity client if URL is provided\n const entityClient = useMemo(\n () => (entityUrl ? getOrCreateEntityClient(entityUrl) : null),\n [entityUrl]\n );\n\n // Get RevenueCat API key from prop or env var\n const rcApiKey = revenueCatApiKey || import.meta.env.VITE_REVENUECAT_API_KEY;\n\n // Create a combined providers component that includes entity support\n const EntityProviders: ComponentType<{ children: ReactNode }> = ({\n children,\n }) => {\n let content = children;\n\n // Wrap with AppProviders if provided\n if (AppProviders) {\n content = <AppProviders>{content}</AppProviders>;\n }\n\n // Wrap with EntityAwareSubscriptionProvider (custom, default, or disabled)\n if (EntityAwareSubscriptionProviderProp === false) {\n // Explicitly disabled - skip subscription provider\n } else if (EntityAwareSubscriptionProviderProp) {\n // Custom provider\n content = (\n <EntityAwareSubscriptionProviderProp>\n {content}\n </EntityAwareSubscriptionProviderProp>\n );\n } else if (rcApiKey) {\n // Default provider with API key\n content = (\n <DefaultEntityAwareSubscriptionProvider apiKey={rcApiKey}>\n {content}\n </DefaultEntityAwareSubscriptionProvider>\n );\n }\n // If no API key and no custom provider, subscription features are silently disabled\n\n // Wrap with AuthAwareEntityProvider (custom or default)\n if (AuthAwareEntityProviderProp) {\n return (\n <AuthAwareEntityProviderProp>{content}</AuthAwareEntityProviderProp>\n );\n }\n\n // Use default if entityClient is available\n if (entityClient) {\n return (\n <DefaultAuthAwareEntityProvider entityClient={entityClient}>\n {content}\n </DefaultAuthAwareEntityProvider>\n );\n }\n\n // No entity support if no entityApiUrl or custom provider\n console.warn(\n '[SudobilityAppWithFirebaseAuthAndEntities] No entityApiUrl or AuthAwareEntityProvider provided - entity features disabled'\n );\n return <>{content}</>;\n };\n\n return (\n <SudobilityAppWithFirebaseAuth\n {...baseProps}\n AppProviders={EntityProviders}\n />\n );\n}\n\nexport default SudobilityAppWithFirebaseAuthAndEntities;\n"],"names":["SubscriptionProviderWrapper"],"mappings":"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"}
1
+ {"version":3,"file":"firebase.js","sources":["../src/components/api/ApiContext.tsx","../src/components/app/SudobilityAppWithFirebaseAuth.tsx","../src/components/subscription/LazySubscriptionProvider.tsx","../src/components/subscription/SubscriptionProviderWrapper.tsx","../src/components/app/SudobilityAppWithFirebaseAuthAndEntities.tsx"],"sourcesContent":["/**\n * API Context and Provider\n *\n * Provides network client, auth token, and API configuration to the app.\n */\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n useMemo,\n useCallback,\n type ReactNode,\n} from 'react';\nimport { networkClient, getInfoService } from '@sudobility/di';\nimport { InfoType } from '@sudobility/types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport { getFirebaseAuth } from '@sudobility/auth_lib';\n\n/**\n * API context value provided to consumers\n */\nexport interface ApiContextValue {\n /** Network client for making API requests */\n networkClient: NetworkClient;\n /** Base URL for API requests */\n baseUrl: string;\n /** Current user ID (Firebase UID) */\n userId: string | null;\n /** Firebase ID token for authenticated requests */\n token: string | null;\n /** Whether API is ready (user authenticated and token available) */\n isReady: boolean;\n /** Whether auth/token is still loading */\n isLoading: boolean;\n /** Force refresh the ID token */\n refreshToken: () => Promise<string | null>;\n /** Whether running in test/sandbox mode */\n testMode: boolean;\n}\n\nconst ApiContext = createContext<ApiContextValue | null>(null);\n\n/**\n * Hook to access API context\n * @throws Error if used outside of ApiProvider\n */\nexport function useApi(): ApiContextValue {\n const context = useContext(ApiContext);\n if (!context) {\n throw new Error('useApi must be used within an ApiProvider');\n }\n return context;\n}\n\n/**\n * Hook to safely access API context (returns null if not available)\n */\nexport function useApiSafe(): ApiContextValue | null {\n return useContext(ApiContext);\n}\n\ninterface ApiProviderProps {\n children: ReactNode;\n /**\n * Base URL for API requests (optional).\n * Defaults to VITE_API_URL env var.\n */\n baseUrl?: string;\n /**\n * Whether running in test/sandbox mode (optional).\n * Defaults to false.\n */\n testMode?: boolean;\n}\n\n/**\n * API Provider\n *\n * Manages Firebase ID token and provides API configuration.\n * Uses VITE_API_URL env var for baseUrl by default.\n */\nexport function ApiProvider({\n children,\n baseUrl: baseUrlProp,\n testMode = false,\n}: ApiProviderProps) {\n const { user, loading: authLoading } = useAuthStatus();\n const [token, setToken] = useState<string | null>(null);\n const [tokenLoading, setTokenLoading] = useState(false);\n const auth = getFirebaseAuth();\n\n const baseUrl = baseUrlProp || import.meta.env.VITE_API_URL || '';\n const userId = user?.uid ?? null;\n\n // Fetch token when user changes\n useEffect(() => {\n let mounted = true;\n\n const fetchToken = async () => {\n if (!userId) {\n setToken(null);\n setTokenLoading(false);\n return;\n }\n\n setTokenLoading(true);\n try {\n const currentUser = auth?.currentUser;\n if (!currentUser) {\n setToken(null);\n return;\n }\n const idToken = await currentUser.getIdToken();\n if (mounted) {\n setToken(idToken);\n }\n } catch {\n try {\n getInfoService().show(\n 'Authentication Error',\n 'Failed to get ID token',\n InfoType.ERROR,\n 5000\n );\n } catch {\n console.error('[ApiProvider] Failed to get ID token');\n }\n if (mounted) {\n setToken(null);\n }\n } finally {\n if (mounted) {\n setTokenLoading(false);\n }\n }\n };\n\n fetchToken();\n\n return () => {\n mounted = false;\n };\n }, [userId, auth]);\n\n // Refresh token function for when token expires\n const refreshToken = useCallback(async (): Promise<string | null> => {\n const currentUser = auth?.currentUser;\n if (!currentUser) return null;\n try {\n const newToken = await currentUser.getIdToken(true); // Force refresh\n setToken(newToken);\n return newToken;\n } catch {\n try {\n getInfoService().show(\n 'Authentication Error',\n 'Failed to refresh ID token',\n InfoType.ERROR,\n 5000\n );\n } catch {\n console.error('[ApiProvider] Failed to refresh ID token');\n }\n setToken(null);\n return null;\n }\n }, [auth]);\n\n const value = useMemo<ApiContextValue>(\n () => ({\n networkClient,\n baseUrl,\n userId,\n token,\n isReady: !!userId && !!token,\n isLoading: authLoading || tokenLoading,\n refreshToken,\n testMode,\n }),\n [baseUrl, userId, token, authLoading, tokenLoading, refreshToken, testMode]\n );\n\n return <ApiContext.Provider value={value}>{children}</ApiContext.Provider>;\n}\n\nexport { ApiContext };\n","/**\n * SudobilityAppWithFirebaseAuth - App wrapper with Firebase authentication\n *\n * Extends SudobilityApp with:\n * - Firebase AuthProviderWrapper for authentication (built-in default)\n * - ApiProvider for network/token management (built-in default)\n */\nimport { ComponentType, ReactNode, useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { SudobilityApp, SudobilityAppProps } from './SudobilityApp';\nimport { AuthProvider } from '@sudobility/auth-components';\nimport { getFirebaseAuth, getFirebaseErrorMessage } from '@sudobility/auth_lib';\nimport { ApiProvider } from '../api';\n\nexport interface SudobilityAppWithFirebaseAuthProps extends Omit<\n SudobilityAppProps,\n 'AppProviders'\n> {\n /**\n * Custom Firebase auth provider wrapper component (optional).\n * Defaults to built-in AuthProviderWrapper that uses getFirebaseAuth().\n */\n AuthProviderWrapper?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Auth providers to enable (optional).\n * Defaults to [\"google\", \"email\"].\n */\n authProviders?: ('google' | 'email' | 'apple')[];\n\n /**\n * Whether to enable anonymous auth (optional).\n * Defaults to false.\n */\n enableAnonymousAuth?: boolean;\n\n /**\n * Custom ApiProvider component (optional).\n * Defaults to built-in ApiProvider that manages Firebase ID token.\n * Set to false to disable the built-in ApiProvider.\n */\n ApiProvider?: ComponentType<{ children: ReactNode }> | false;\n\n /**\n * Additional providers to wrap around the router content.\n * These are rendered inside ApiProvider but outside BrowserRouter.\n */\n AppProviders?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Whether running in test/sandbox mode (optional).\n * Passed to ApiProvider. Defaults to false.\n */\n testMode?: boolean;\n\n /**\n * Base URL for API requests (optional).\n * Passed to ApiProvider. Defaults to VITE_API_URL env var.\n */\n baseUrl?: string;\n}\n\n/**\n * Default auth texts - minimal fallback when translations aren't available\n */\nfunction createDefaultAuthTexts(t: (key: string) => string) {\n return {\n signInTitle: t('auth.signInTitle') || 'Sign In',\n signInWithEmail: t('auth.signInWithEmail') || 'Sign in with email',\n createAccount: t('auth.createAccount') || 'Create Account',\n resetPassword: t('auth.resetPassword') || 'Reset Password',\n signIn: t('auth.signIn') || 'Sign In',\n signUp: t('auth.signUp') || 'Sign Up',\n logout: t('auth.logout') || 'Log Out',\n login: t('auth.login') || 'Log In',\n continueWithGoogle: t('auth.continueWithGoogle') || 'Continue with Google',\n continueWithApple: t('auth.continueWithApple') || 'Continue with Apple',\n continueWithEmail: t('auth.continueWithEmail') || 'Continue with Email',\n sendResetLink: t('auth.sendResetLink') || 'Send Reset Link',\n backToSignIn: t('auth.backToSignIn') || 'Back to Sign In',\n close: t('auth.close') || 'Close',\n email: t('auth.email') || 'Email',\n password: t('auth.password') || 'Password',\n confirmPassword: t('auth.confirmPassword') || 'Confirm Password',\n displayName: t('auth.displayName') || 'Display Name',\n emailPlaceholder: t('auth.emailPlaceholder') || 'Enter your email',\n passwordPlaceholder: t('auth.passwordPlaceholder') || 'Enter your password',\n confirmPasswordPlaceholder:\n t('auth.confirmPasswordPlaceholder') || 'Confirm your password',\n displayNamePlaceholder:\n t('auth.displayNamePlaceholder') || 'Enter your name',\n forgotPassword: t('auth.forgotPassword') || 'Forgot Password?',\n noAccount: t('auth.noAccount') || \"Don't have an account?\",\n haveAccount: t('auth.haveAccount') || 'Already have an account?',\n or: t('auth.or') || 'or',\n resetEmailSent: t('auth.resetEmailSent') || 'Email Sent',\n resetEmailSentDesc:\n t('auth.resetEmailSentDesc') ||\n 'Check your inbox for the password reset link.',\n passwordMismatch: t('auth.passwordMismatch') || \"Passwords don't match\",\n passwordTooShort:\n t('auth.passwordTooShort') || 'Password must be at least 6 characters',\n loading: t('auth.loading') || 'Loading...',\n };\n}\n\n/**\n * Default auth error texts\n */\nfunction createDefaultAuthErrorTexts() {\n return {\n 'auth/user-not-found': 'No account found with this email',\n 'auth/wrong-password': 'Incorrect password',\n 'auth/invalid-email': 'Please enter a valid email address',\n 'auth/invalid-credential': 'Invalid credentials',\n 'auth/email-already-in-use': 'An account already exists with this email',\n 'auth/weak-password': 'Password should be at least 6 characters',\n 'auth/too-many-requests': 'Too many attempts. Please try again later.',\n 'auth/network-request-failed':\n 'Network error. Please check your connection.',\n 'auth/popup-closed-by-user': 'Sign in was cancelled',\n 'auth/popup-blocked':\n 'Pop-up was blocked. Please allow pop-ups and try again.',\n 'auth/account-exists-with-different-credential':\n 'An account already exists with this email using a different sign-in method',\n 'auth/operation-not-allowed': 'This sign-in method is not enabled',\n default: 'An error occurred. Please try again.',\n };\n}\n\n/**\n * Default AuthProviderWrapper using Firebase\n */\nfunction DefaultAuthProviderWrapper({\n children,\n providers,\n enableAnonymous,\n}: {\n children: ReactNode;\n providers: ('google' | 'email' | 'apple')[];\n enableAnonymous: boolean;\n}) {\n const { t } = useTranslation();\n\n const texts = useMemo(() => createDefaultAuthTexts(t), [t]);\n const errorTexts = useMemo(() => createDefaultAuthErrorTexts(), []);\n\n const auth = getFirebaseAuth();\n\n // If Firebase is not configured, render children without auth\n if (!auth) {\n console.warn(\n '[SudobilityAppWithFirebaseAuth] No auth instance - Firebase not configured'\n );\n return <>{children}</>;\n }\n\n return (\n <AuthProvider\n firebaseConfig={{ type: 'instance', auth: auth }}\n providerConfig={{\n providers,\n enableAnonymous,\n }}\n texts={texts}\n errorTexts={errorTexts}\n resolveErrorMessage={getFirebaseErrorMessage}\n >\n {children}\n </AuthProvider>\n );\n}\n\n/**\n * SudobilityAppWithFirebaseAuth - App wrapper with Firebase authentication\n *\n * @example\n * ```tsx\n * // Minimal usage - uses default auth with Google and Email\n * import { SudobilityAppWithFirebaseAuth } from '@sudobility/building_blocks';\n * import i18n from './i18n';\n *\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuth i18n={i18n}>\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuth>\n * );\n * }\n *\n * // With custom auth providers\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuth\n * i18n={i18n}\n * authProviders={[\"google\", \"apple\", \"email\"]}\n * AppProviders={ApiProvider}\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuth>\n * );\n * }\n * ```\n */\nexport function SudobilityAppWithFirebaseAuth({\n AuthProviderWrapper: AuthProviderWrapperProp,\n authProviders = ['google', 'email'],\n enableAnonymousAuth = false,\n ApiProvider: ApiProviderProp,\n AppProviders,\n testMode = false,\n baseUrl,\n ...baseProps\n}: SudobilityAppWithFirebaseAuthProps) {\n // Create a combined providers component that includes auth and api\n const CombinedProviders: ComponentType<{ children: ReactNode }> = ({\n children,\n }) => {\n let content = children;\n\n // Wrap with AppProviders if provided\n if (AppProviders) {\n content = <AppProviders>{content}</AppProviders>;\n }\n\n // Wrap with ApiProvider (custom, default, or disabled)\n if (ApiProviderProp === false) {\n // Explicitly disabled\n } else if (ApiProviderProp) {\n content = <ApiProviderProp>{content}</ApiProviderProp>;\n } else {\n // Default ApiProvider\n content = (\n <ApiProvider baseUrl={baseUrl} testMode={testMode}>\n {content}\n </ApiProvider>\n );\n }\n\n // Wrap with AuthProviderWrapper (custom or default)\n if (AuthProviderWrapperProp) {\n return <AuthProviderWrapperProp>{content}</AuthProviderWrapperProp>;\n }\n\n return (\n <DefaultAuthProviderWrapper\n providers={authProviders}\n enableAnonymous={enableAnonymousAuth}\n >\n {content}\n </DefaultAuthProviderWrapper>\n );\n };\n\n return <SudobilityApp {...baseProps} AppProviders={CombinedProviders} />;\n}\n\nexport default SudobilityAppWithFirebaseAuth;\n","/**\n * Lazy Subscription Provider\n *\n * Defers loading of RevenueCat SDK (~600KB) until user is authenticated.\n * For unauthenticated users, provides a stub context.\n */\n\nimport { type ReactNode, Suspense, lazy, useMemo } from 'react';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport {\n SafeSubscriptionContext,\n STUB_SUBSCRIPTION_VALUE,\n} from './SafeSubscriptionContext';\n\n// Lazy load the actual subscription provider\nconst SubscriptionProviderWrapper = lazy(\n () => import('./SubscriptionProviderWrapper')\n);\n\nfunction StubSubscriptionProvider({ children }: { children: ReactNode }) {\n return (\n <SafeSubscriptionContext.Provider value={STUB_SUBSCRIPTION_VALUE}>\n {children}\n </SafeSubscriptionContext.Provider>\n );\n}\n\ninterface LazySubscriptionProviderProps {\n children: ReactNode;\n /** Entity ID to use as RevenueCat subscriber */\n entityId?: string;\n /** RevenueCat API key */\n apiKey: string;\n}\n\n/**\n * Lazy wrapper for SubscriptionProvider that only loads RevenueCat SDK\n * when the user is authenticated. This saves ~600KB on initial load.\n * For unauthenticated users, provides a stub context so hooks don't throw.\n */\nexport function LazySubscriptionProvider({\n children,\n entityId,\n apiKey,\n}: LazySubscriptionProviderProps) {\n const { user } = useAuthStatus();\n\n const isAuthenticated = useMemo(() => {\n return !!user && !user.isAnonymous;\n }, [user]);\n\n if (!isAuthenticated) {\n return <StubSubscriptionProvider>{children}</StubSubscriptionProvider>;\n }\n\n return (\n <Suspense\n fallback={<StubSubscriptionProvider>{children}</StubSubscriptionProvider>}\n >\n <SubscriptionProviderWrapper entityId={entityId} apiKey={apiKey}>\n {children}\n </SubscriptionProviderWrapper>\n </Suspense>\n );\n}\n\nexport default LazySubscriptionProvider;\n","/**\n * Subscription Provider Wrapper\n *\n * Integrates subscription-components with auth and sets up RevenueCat user.\n */\n\nimport { type ReactNode, useEffect, useRef } from 'react';\nimport {\n SubscriptionProvider,\n useSubscriptionContext,\n} from '@sudobility/subscription-components';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport { getInfoService } from '@sudobility/di';\nimport { InfoType } from '@sudobility/types';\nimport {\n setRevenueCatUser,\n clearRevenueCatUser,\n refreshSubscription,\n} from '@sudobility/subscription_lib';\nimport { SafeSubscriptionContext } from './SafeSubscriptionContext';\n\ninterface SubscriptionProviderWrapperProps {\n children: ReactNode;\n entityId?: string;\n apiKey: string;\n}\n\nconst handleSubscriptionError = (error: Error) => {\n try {\n getInfoService().show(\n 'Subscription Error',\n error.message,\n InfoType.ERROR,\n 5000\n );\n } catch {\n // InfoService not available - log to console\n console.error('[Subscription]', error.message);\n }\n};\n\n/**\n * Bridge that provides context and configures RevenueCat user.\n */\nfunction SubscriptionBridge({\n children,\n entityId,\n}: {\n children: ReactNode;\n entityId?: string;\n}) {\n const { user } = useAuthStatus();\n const context = useSubscriptionContext();\n const entityIdRef = useRef<string | null>(null);\n\n useEffect(() => {\n const shouldSetUser = user && !user.isAnonymous && entityId;\n\n if (shouldSetUser && entityId !== entityIdRef.current) {\n entityIdRef.current = entityId;\n // Set user for both subscription-components and subscription_lib\n context.initialize(entityId, user.email || undefined);\n setRevenueCatUser(entityId, user.email || undefined).then(() => {\n // Refresh subscription_lib data after user is set\n refreshSubscription();\n });\n } else if (!shouldSetUser && entityIdRef.current) {\n entityIdRef.current = null;\n clearRevenueCatUser();\n }\n }, [user, entityId, context]);\n\n return (\n <SafeSubscriptionContext.Provider value={context}>\n {children}\n </SafeSubscriptionContext.Provider>\n );\n}\n\nexport function SubscriptionProviderWrapper({\n children,\n entityId,\n apiKey,\n}: SubscriptionProviderWrapperProps) {\n const { user } = useAuthStatus();\n\n return (\n <SubscriptionProvider\n apiKey={apiKey}\n userEmail={user?.email || undefined}\n onError={handleSubscriptionError}\n >\n <SubscriptionBridge entityId={entityId}>{children}</SubscriptionBridge>\n </SubscriptionProvider>\n );\n}\n\nexport default SubscriptionProviderWrapper;\n","/**\n * SudobilityAppWithFirebaseAuthAndEntities - App wrapper with Firebase auth and entity support\n *\n * Extends SudobilityAppWithFirebaseAuth with:\n * - AuthAwareEntityProvider that connects entity context to auth state (built-in default)\n * - EntityAwareSubscriptionProvider that connects subscription to entity (built-in default)\n */\nimport { ComponentType, ReactNode, useMemo } from 'react';\nimport {\n SudobilityAppWithFirebaseAuth,\n SudobilityAppWithFirebaseAuthProps,\n} from './SudobilityAppWithFirebaseAuth';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport {\n CurrentEntityProvider,\n EntityClient,\n useCurrentEntity,\n} from '@sudobility/entity_client';\nimport { getFirebaseAuth } from '@sudobility/auth_lib';\nimport { LazySubscriptionProvider } from '../subscription';\n\nexport interface SudobilityAppWithFirebaseAuthAndEntitiesProps extends Omit<\n SudobilityAppWithFirebaseAuthProps,\n 'AppProviders'\n> {\n /**\n * Base URL for the API (optional).\n * Defaults to VITE_API_URL env var.\n * Used for both ApiProvider and EntityClient.\n *\n * @example \"https://api.myapp.com\"\n */\n apiUrl?: string;\n\n /**\n * @deprecated Use apiUrl instead. Will be removed in future version.\n */\n entityApiUrl?: string;\n\n /**\n * RevenueCat API key for production subscriptions (optional).\n * If not provided, reads from VITE_REVENUECAT_API_KEY env var.\n * If neither is available, subscription features are disabled.\n */\n revenueCatApiKey?: string;\n\n /**\n * RevenueCat API key for sandbox/test subscriptions (optional).\n * Used when testMode is true.\n * If not provided, reads from VITE_REVENUECAT_API_KEY_SANDBOX env var.\n */\n revenueCatApiKeySandbox?: string;\n\n /**\n * Custom AuthAwareEntityProvider component (optional).\n * Defaults to built-in provider if entityApiUrl is provided.\n */\n AuthAwareEntityProvider?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Custom EntityAwareSubscriptionProvider component (optional).\n * Defaults to built-in provider that uses LazySubscriptionProvider.\n * Set to false to disable subscription features entirely.\n */\n EntityAwareSubscriptionProvider?:\n | ComponentType<{ children: ReactNode }>\n | false;\n\n /**\n * Additional providers to wrap around the router content.\n * These are rendered inside EntityAwareSubscriptionProvider but outside BrowserRouter.\n * Use this for app-specific providers like ApiProvider.\n */\n AppProviders?: ComponentType<{ children: ReactNode }>;\n}\n\n/**\n * Get Firebase auth token for API requests\n */\nasync function getAuthToken(): Promise<string | null> {\n const auth = getFirebaseAuth();\n const currentUser = auth?.currentUser;\n if (!currentUser) {\n return null;\n }\n try {\n return await currentUser.getIdToken();\n } catch {\n return null;\n }\n}\n\n/**\n * Create a default entity client for the given API URL\n */\nfunction createEntityClient(baseUrl: string): EntityClient {\n return new EntityClient({\n baseUrl,\n getAuthToken,\n });\n}\n\n// Cache for entity clients by URL\nconst entityClientCache = new Map<string, EntityClient>();\n\nfunction getOrCreateEntityClient(baseUrl: string): EntityClient {\n let client = entityClientCache.get(baseUrl);\n if (!client) {\n client = createEntityClient(baseUrl);\n entityClientCache.set(baseUrl, client);\n }\n return client;\n}\n\n/**\n * Default AuthAwareEntityProvider using CurrentEntityProvider\n */\nfunction DefaultAuthAwareEntityProvider({\n children,\n entityClient,\n}: {\n children: ReactNode;\n entityClient: EntityClient;\n}) {\n const { user } = useAuthStatus();\n const authUser = useMemo(\n () => (user ? { uid: user.uid, email: user.email } : null),\n [user]\n );\n\n return (\n <CurrentEntityProvider client={entityClient} user={authUser}>\n {children}\n </CurrentEntityProvider>\n );\n}\n\n/**\n * Default EntityAwareSubscriptionProvider using LazySubscriptionProvider\n */\nfunction DefaultEntityAwareSubscriptionProvider({\n children,\n apiKey,\n}: {\n children: ReactNode;\n apiKey: string;\n}) {\n const { currentEntityId } = useCurrentEntity();\n return (\n <LazySubscriptionProvider\n entityId={currentEntityId ?? undefined}\n apiKey={apiKey}\n >\n {children}\n </LazySubscriptionProvider>\n );\n}\n\n/**\n * SudobilityAppWithFirebaseAuthAndEntities - Full-featured app wrapper\n *\n * @example\n * ```tsx\n * // Minimal usage with entityApiUrl - uses all defaults\n * import { SudobilityAppWithFirebaseAuthAndEntities } from '@sudobility/building_blocks';\n * import i18n from './i18n';\n *\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuthAndEntities\n * i18n={i18n}\n * entityApiUrl=\"https://api.myapp.com/api/v1\"\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuthAndEntities>\n * );\n * }\n *\n * // With custom providers\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuthAndEntities\n * i18n={i18n}\n * AuthAwareEntityProvider={MyEntityProvider}\n * EntityAwareSubscriptionProvider={MySubscriptionProvider}\n * AppProviders={ApiProvider}\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuthAndEntities>\n * );\n * }\n * ```\n */\nexport function SudobilityAppWithFirebaseAuthAndEntities({\n apiUrl,\n entityApiUrl, // deprecated, use apiUrl\n revenueCatApiKey,\n revenueCatApiKeySandbox,\n AuthAwareEntityProvider: AuthAwareEntityProviderProp,\n EntityAwareSubscriptionProvider: EntityAwareSubscriptionProviderProp,\n AppProviders,\n testMode = false,\n ...baseProps\n}: SudobilityAppWithFirebaseAuthAndEntitiesProps) {\n // Get API URL from prop or env var\n // Support deprecated entityApiUrl for backwards compatibility\n const baseApiUrl = apiUrl || import.meta.env.VITE_API_URL || '';\n const entityUrl = entityApiUrl || (baseApiUrl ? `${baseApiUrl}/api/v1` : '');\n\n // Get or create entity client if URL is provided\n const entityClient = useMemo(\n () => (entityUrl ? getOrCreateEntityClient(entityUrl) : null),\n [entityUrl]\n );\n\n // Get RevenueCat API key from prop or env var, selecting based on testMode\n const rcApiKeyProd =\n revenueCatApiKey || import.meta.env.VITE_REVENUECAT_API_KEY || '';\n const rcApiKeySandbox =\n revenueCatApiKeySandbox ||\n import.meta.env.VITE_REVENUECAT_API_KEY_SANDBOX ||\n '';\n const rcApiKey = testMode ? rcApiKeySandbox : rcApiKeyProd;\n\n // Create a combined providers component that includes entity support\n const EntityProviders: ComponentType<{ children: ReactNode }> = ({\n children,\n }) => {\n let content = children;\n\n // Wrap with AppProviders if provided\n if (AppProviders) {\n content = <AppProviders>{content}</AppProviders>;\n }\n\n // Wrap with EntityAwareSubscriptionProvider (custom, default, or disabled)\n if (EntityAwareSubscriptionProviderProp === false) {\n // Explicitly disabled - skip subscription provider\n } else if (EntityAwareSubscriptionProviderProp) {\n // Custom provider\n content = (\n <EntityAwareSubscriptionProviderProp>\n {content}\n </EntityAwareSubscriptionProviderProp>\n );\n } else if (rcApiKey) {\n // Default provider with API key\n content = (\n <DefaultEntityAwareSubscriptionProvider apiKey={rcApiKey}>\n {content}\n </DefaultEntityAwareSubscriptionProvider>\n );\n }\n // If no API key and no custom provider, subscription features are silently disabled\n\n // Wrap with AuthAwareEntityProvider (custom or default)\n if (AuthAwareEntityProviderProp) {\n return (\n <AuthAwareEntityProviderProp>{content}</AuthAwareEntityProviderProp>\n );\n }\n\n // Use default if entityClient is available\n if (entityClient) {\n return (\n <DefaultAuthAwareEntityProvider entityClient={entityClient}>\n {content}\n </DefaultAuthAwareEntityProvider>\n );\n }\n\n // No entity support if no entityApiUrl or custom provider\n console.warn(\n '[SudobilityAppWithFirebaseAuthAndEntities] No entityApiUrl or AuthAwareEntityProvider provided - entity features disabled'\n );\n return <>{content}</>;\n };\n\n return (\n <SudobilityAppWithFirebaseAuth\n {...baseProps}\n baseUrl={baseApiUrl}\n testMode={testMode}\n AppProviders={EntityProviders}\n />\n );\n}\n\nexport default SudobilityAppWithFirebaseAuthAndEntities;\n"],"names":["SubscriptionProviderWrapper"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/building_blocks",
3
- "version": "0.0.80",
3
+ "version": "0.0.83",
4
4
  "description": "Higher-level shared UI building blocks for Sudobility apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",