@sudobility/building_blocks 0.0.115 → 0.0.117
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/dist/SafeSubscriptionContext-BuMPCXVi.js +255 -0
- package/dist/SafeSubscriptionContext-BuMPCXVi.js.map +1 -0
- package/dist/firebase.js +2 -6
- package/dist/firebase.js.map +1 -1
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/SafeSubscriptionContext-yobdw2tN.js +0 -16053
- package/dist/SafeSubscriptionContext-yobdw2tN.js.map +0 -1
- package/dist/browser-ponyfill-Bi0tvFyy.js +0 -557
- package/dist/browser-ponyfill-Bi0tvFyy.js.map +0 -1
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense, useRef, useEffect, useMemo, createContext, useContext } from "react";
|
|
3
|
+
import { useLocation, BrowserRouter } from "react-router-dom";
|
|
4
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
5
|
+
import { HelmetProvider } from "react-helmet-async";
|
|
6
|
+
import { initReactI18next, I18nextProvider } from "react-i18next";
|
|
7
|
+
import { NetworkProvider } from "@sudobility/devops-components";
|
|
8
|
+
import { getFirebaseService, getNetworkService } from "@sudobility/di/web";
|
|
9
|
+
import { InfoBanner } from "@sudobility/di_web";
|
|
10
|
+
import { ThemeProvider, FontSize, Theme } from "@sudobility/components";
|
|
11
|
+
import { useToast, ToastContainer, ToastProvider } from "@sudobility/components/ui/toast";
|
|
12
|
+
import i18n from "i18next";
|
|
13
|
+
import Backend from "i18next-http-backend";
|
|
14
|
+
import LanguageDetector from "i18next-browser-languagedetector";
|
|
15
|
+
const DEFAULT_SUPPORTED_LANGUAGES = ["en"];
|
|
16
|
+
const DEFAULT_NAMESPACES = [
|
|
17
|
+
"common",
|
|
18
|
+
"home",
|
|
19
|
+
"pricing",
|
|
20
|
+
"docs",
|
|
21
|
+
"dashboard",
|
|
22
|
+
"auth",
|
|
23
|
+
"privacy",
|
|
24
|
+
"terms",
|
|
25
|
+
"settings"
|
|
26
|
+
];
|
|
27
|
+
function detectLanguageFromPath(supportedLanguages) {
|
|
28
|
+
if (typeof window === "undefined") {
|
|
29
|
+
return "en";
|
|
30
|
+
}
|
|
31
|
+
const pathLang = window.location.pathname.split("/")[1];
|
|
32
|
+
if (pathLang && supportedLanguages.includes(pathLang)) {
|
|
33
|
+
return pathLang;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const stored = localStorage.getItem("language");
|
|
37
|
+
if (stored && supportedLanguages.includes(stored)) {
|
|
38
|
+
return stored;
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
return "en";
|
|
43
|
+
}
|
|
44
|
+
let initialized = false;
|
|
45
|
+
function initializeI18n(config = {}) {
|
|
46
|
+
if (initialized) {
|
|
47
|
+
return i18n;
|
|
48
|
+
}
|
|
49
|
+
initialized = true;
|
|
50
|
+
const {
|
|
51
|
+
supportedLanguages = DEFAULT_SUPPORTED_LANGUAGES,
|
|
52
|
+
namespaces = DEFAULT_NAMESPACES,
|
|
53
|
+
defaultNamespace = "common",
|
|
54
|
+
loadPath = "/locales/{{lng}}/{{ns}}.json",
|
|
55
|
+
debug = false
|
|
56
|
+
} = config;
|
|
57
|
+
i18n.use(Backend).use(LanguageDetector).use(initReactI18next).init({
|
|
58
|
+
lng: detectLanguageFromPath(supportedLanguages),
|
|
59
|
+
fallbackLng: {
|
|
60
|
+
zh: ["zh", "en"],
|
|
61
|
+
"zh-hant": ["zh-hant", "zh", "en"],
|
|
62
|
+
default: ["en"]
|
|
63
|
+
},
|
|
64
|
+
initImmediate: false,
|
|
65
|
+
supportedLngs: supportedLanguages,
|
|
66
|
+
debug,
|
|
67
|
+
nonExplicitSupportedLngs: true,
|
|
68
|
+
interpolation: {
|
|
69
|
+
escapeValue: false
|
|
70
|
+
},
|
|
71
|
+
backend: {
|
|
72
|
+
loadPath
|
|
73
|
+
},
|
|
74
|
+
detection: {
|
|
75
|
+
order: ["path", "localStorage", "navigator"],
|
|
76
|
+
caches: ["localStorage"],
|
|
77
|
+
lookupLocalStorage: "language",
|
|
78
|
+
lookupFromPathIndex: 0
|
|
79
|
+
},
|
|
80
|
+
load: "languageOnly",
|
|
81
|
+
preload: [],
|
|
82
|
+
cleanCode: false,
|
|
83
|
+
lowerCaseLng: false,
|
|
84
|
+
defaultNS: defaultNamespace,
|
|
85
|
+
ns: namespaces
|
|
86
|
+
});
|
|
87
|
+
return i18n;
|
|
88
|
+
}
|
|
89
|
+
function getI18n() {
|
|
90
|
+
if (!initialized) {
|
|
91
|
+
initializeI18n();
|
|
92
|
+
}
|
|
93
|
+
return i18n;
|
|
94
|
+
}
|
|
95
|
+
function DefaultLoadingFallback() {
|
|
96
|
+
return /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center bg-theme-bg-primary", children: /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" }) });
|
|
97
|
+
}
|
|
98
|
+
function DefaultPageTracker() {
|
|
99
|
+
const location = useLocation();
|
|
100
|
+
const previousPathRef = useRef(null);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
var _a;
|
|
103
|
+
const currentPath = location.pathname;
|
|
104
|
+
if (previousPathRef.current === currentPath) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
previousPathRef.current = currentPath;
|
|
108
|
+
try {
|
|
109
|
+
const firebaseService = getFirebaseService();
|
|
110
|
+
if ((_a = firebaseService == null ? void 0 : firebaseService.analytics) == null ? void 0 : _a.isSupported()) {
|
|
111
|
+
firebaseService.analytics.logEvent("page_view", {
|
|
112
|
+
page_path: currentPath,
|
|
113
|
+
page_location: window.location.href
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
}, [location.pathname]);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
function DefaultNetworkProvider({ children }) {
|
|
122
|
+
const networkService = useMemo(() => getNetworkService(), []);
|
|
123
|
+
return /* @__PURE__ */ jsx(NetworkProvider, { networkService, children });
|
|
124
|
+
}
|
|
125
|
+
function createDefaultQueryClient() {
|
|
126
|
+
return new QueryClient({
|
|
127
|
+
defaultOptions: {
|
|
128
|
+
queries: {
|
|
129
|
+
staleTime: 5 * 60 * 1e3,
|
|
130
|
+
// 5 minutes
|
|
131
|
+
gcTime: 10 * 60 * 1e3,
|
|
132
|
+
// 10 minutes
|
|
133
|
+
// Smart retry: don't retry on 4xx client errors, retry up to 3 times otherwise
|
|
134
|
+
retry: (failureCount, error) => {
|
|
135
|
+
const statusCode = error == null ? void 0 : error.statusCode;
|
|
136
|
+
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return failureCount < 3;
|
|
140
|
+
},
|
|
141
|
+
// Exponential backoff: 1s, 2s, 4s... up to 30s
|
|
142
|
+
retryDelay: (attemptIndex) => Math.min(1e3 * 2 ** attemptIndex, 3e4),
|
|
143
|
+
refetchOnWindowFocus: true
|
|
144
|
+
},
|
|
145
|
+
mutations: {
|
|
146
|
+
retry: false
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
let defaultQueryClient = null;
|
|
152
|
+
function getDefaultQueryClient() {
|
|
153
|
+
if (!defaultQueryClient) {
|
|
154
|
+
defaultQueryClient = createDefaultQueryClient();
|
|
155
|
+
}
|
|
156
|
+
return defaultQueryClient;
|
|
157
|
+
}
|
|
158
|
+
function createDefaultThemeProvider(storageKeyPrefix) {
|
|
159
|
+
return function DefaultThemeProvider({ children }) {
|
|
160
|
+
return /* @__PURE__ */ jsx(
|
|
161
|
+
ThemeProvider,
|
|
162
|
+
{
|
|
163
|
+
themeStorageKey: `${storageKeyPrefix}-theme`,
|
|
164
|
+
fontSizeStorageKey: `${storageKeyPrefix}-font-size`,
|
|
165
|
+
defaultTheme: Theme.LIGHT,
|
|
166
|
+
defaultFontSize: FontSize.MEDIUM,
|
|
167
|
+
children
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function DefaultToastProvider({ children }) {
|
|
173
|
+
return /* @__PURE__ */ jsx(ToastProvider, { children });
|
|
174
|
+
}
|
|
175
|
+
function DefaultToastContainer() {
|
|
176
|
+
const { toasts, removeToast } = useToast();
|
|
177
|
+
if (toasts.length === 0) return null;
|
|
178
|
+
return /* @__PURE__ */ jsx(
|
|
179
|
+
ToastContainer,
|
|
180
|
+
{
|
|
181
|
+
toasts,
|
|
182
|
+
onDismiss: removeToast,
|
|
183
|
+
position: "bottom-right"
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
function SudobilityApp({
|
|
188
|
+
children,
|
|
189
|
+
i18n: i18nInstance,
|
|
190
|
+
queryClient,
|
|
191
|
+
ThemeProvider: ThemeProviderProp,
|
|
192
|
+
ToastProvider: ToastProviderProp,
|
|
193
|
+
ToastContainer: ToastContainer2,
|
|
194
|
+
showInfoBanner = true,
|
|
195
|
+
LoadingFallback = DefaultLoadingFallback,
|
|
196
|
+
NetworkProvider: NetworkProviderProp,
|
|
197
|
+
PageTracker: PageTrackerProp,
|
|
198
|
+
AppProviders,
|
|
199
|
+
storageKeyPrefix = "sudobility",
|
|
200
|
+
RouterWrapper
|
|
201
|
+
}) {
|
|
202
|
+
const i18nToUse = i18nInstance ?? getI18n();
|
|
203
|
+
const NetworkProviderComponent = NetworkProviderProp === false ? null : NetworkProviderProp ?? DefaultNetworkProvider;
|
|
204
|
+
const PageTrackerComponent = PageTrackerProp === false ? null : PageTrackerProp ?? DefaultPageTracker;
|
|
205
|
+
const ThemeProviderComponent = ThemeProviderProp ?? createDefaultThemeProvider(storageKeyPrefix);
|
|
206
|
+
const ToastProviderComponent = ToastProviderProp ?? DefaultToastProvider;
|
|
207
|
+
const ToastContainerComponent = ToastContainer2 ?? DefaultToastContainer;
|
|
208
|
+
const queryClientInstance = queryClient ?? getDefaultQueryClient();
|
|
209
|
+
const RouterWrapperComponent = RouterWrapper ?? BrowserRouter;
|
|
210
|
+
let routerContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
211
|
+
PageTrackerComponent && /* @__PURE__ */ jsx(PageTrackerComponent, {}),
|
|
212
|
+
/* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingFallback, {}), children }),
|
|
213
|
+
/* @__PURE__ */ jsx(ToastContainerComponent, {}),
|
|
214
|
+
showInfoBanner && /* @__PURE__ */ jsx(InfoBanner, {})
|
|
215
|
+
] });
|
|
216
|
+
routerContent = /* @__PURE__ */ jsx(RouterWrapperComponent, { children: routerContent });
|
|
217
|
+
if (AppProviders) {
|
|
218
|
+
routerContent = /* @__PURE__ */ jsx(AppProviders, { children: routerContent });
|
|
219
|
+
}
|
|
220
|
+
routerContent = /* @__PURE__ */ jsx(ToastProviderComponent, { children: routerContent });
|
|
221
|
+
routerContent = /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClientInstance, children: routerContent });
|
|
222
|
+
if (NetworkProviderComponent) {
|
|
223
|
+
routerContent = /* @__PURE__ */ jsx(NetworkProviderComponent, { children: routerContent });
|
|
224
|
+
}
|
|
225
|
+
routerContent = /* @__PURE__ */ jsx(ThemeProviderComponent, { children: routerContent });
|
|
226
|
+
routerContent = /* @__PURE__ */ jsx(I18nextProvider, { i18n: i18nToUse, children: routerContent });
|
|
227
|
+
return /* @__PURE__ */ jsx(HelmetProvider, { children: routerContent });
|
|
228
|
+
}
|
|
229
|
+
const STUB_SUBSCRIPTION_VALUE = {
|
|
230
|
+
products: [],
|
|
231
|
+
currentSubscription: null,
|
|
232
|
+
isLoading: false,
|
|
233
|
+
error: null,
|
|
234
|
+
initialize: () => Promise.resolve(),
|
|
235
|
+
purchase: () => Promise.resolve(false),
|
|
236
|
+
restore: () => Promise.resolve(false),
|
|
237
|
+
refresh: () => Promise.resolve(),
|
|
238
|
+
clearError: () => {
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const SafeSubscriptionContext = createContext(
|
|
242
|
+
STUB_SUBSCRIPTION_VALUE
|
|
243
|
+
);
|
|
244
|
+
function useSafeSubscription() {
|
|
245
|
+
return useContext(SafeSubscriptionContext);
|
|
246
|
+
}
|
|
247
|
+
export {
|
|
248
|
+
SudobilityApp as S,
|
|
249
|
+
SafeSubscriptionContext as a,
|
|
250
|
+
STUB_SUBSCRIPTION_VALUE as b,
|
|
251
|
+
getI18n as g,
|
|
252
|
+
initializeI18n as i,
|
|
253
|
+
useSafeSubscription as u
|
|
254
|
+
};
|
|
255
|
+
//# sourceMappingURL=SafeSubscriptionContext-BuMPCXVi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SafeSubscriptionContext-BuMPCXVi.js","sources":["../src/i18n/index.ts","../src/components/app/SudobilityApp.tsx","../src/components/subscription/SafeSubscriptionContext.tsx"],"sourcesContent":["/**\n * Default i18n configuration for Sudobility apps\n *\n * Creates a pre-configured i18next instance with:\n * - HTTP backend for loading translations from /locales/\n * - Language detection from URL path, localStorage, navigator\n * - React integration\n */\n\nimport i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\nimport Backend from 'i18next-http-backend';\nimport LanguageDetector from 'i18next-browser-languagedetector';\n\n// Default supported languages\nconst DEFAULT_SUPPORTED_LANGUAGES = ['en'];\n\n// Default namespaces\nconst DEFAULT_NAMESPACES = [\n 'common',\n 'home',\n 'pricing',\n 'docs',\n 'dashboard',\n 'auth',\n 'privacy',\n 'terms',\n 'settings',\n];\n\n/**\n * Detect language from URL path first, then localStorage\n */\nfunction detectLanguageFromPath(supportedLanguages: string[]): string {\n if (typeof window === 'undefined') {\n return 'en';\n }\n\n // Check URL path first\n const pathLang = window.location.pathname.split('/')[1];\n if (pathLang && supportedLanguages.includes(pathLang)) {\n return pathLang;\n }\n\n // Fall back to localStorage\n try {\n const stored = localStorage.getItem('language');\n if (stored && supportedLanguages.includes(stored)) {\n return stored;\n }\n } catch {\n // localStorage may throw in Safari private browsing\n }\n\n return 'en';\n}\n\nexport interface I18nConfig {\n /**\n * Supported language codes.\n * Defaults to [\"en\"].\n */\n supportedLanguages?: string[];\n\n /**\n * Translation namespaces to load.\n * Defaults to common app namespaces.\n */\n namespaces?: string[];\n\n /**\n * Default namespace.\n * Defaults to \"common\".\n */\n defaultNamespace?: string;\n\n /**\n * Path pattern for loading translations.\n * Defaults to \"/locales/{{lng}}/{{ns}}.json\".\n */\n loadPath?: string;\n\n /**\n * Enable debug logging.\n * Defaults to false.\n */\n debug?: boolean;\n}\n\nlet initialized = false;\n\n/**\n * Initialize the default i18n instance.\n * Safe to call multiple times - only initializes once.\n */\nexport function initializeI18n(config: I18nConfig = {}): typeof i18n {\n if (initialized) {\n return i18n;\n }\n initialized = true;\n\n const {\n supportedLanguages = DEFAULT_SUPPORTED_LANGUAGES,\n namespaces = DEFAULT_NAMESPACES,\n defaultNamespace = 'common',\n loadPath = '/locales/{{lng}}/{{ns}}.json',\n debug = false,\n } = config;\n\n i18n\n .use(Backend)\n .use(LanguageDetector)\n .use(initReactI18next)\n .init({\n lng: detectLanguageFromPath(supportedLanguages),\n fallbackLng: {\n zh: ['zh', 'en'],\n 'zh-hant': ['zh-hant', 'zh', 'en'],\n default: ['en'],\n },\n initImmediate: false,\n supportedLngs: supportedLanguages,\n debug,\n nonExplicitSupportedLngs: true,\n\n interpolation: {\n escapeValue: false,\n },\n\n backend: {\n loadPath,\n },\n\n detection: {\n order: ['path', 'localStorage', 'navigator'],\n caches: ['localStorage'],\n lookupLocalStorage: 'language',\n lookupFromPathIndex: 0,\n },\n\n load: 'languageOnly',\n preload: [],\n cleanCode: false,\n lowerCaseLng: false,\n\n defaultNS: defaultNamespace,\n ns: namespaces,\n });\n\n return i18n;\n}\n\n/**\n * Get the i18n instance.\n * Initializes with defaults if not already initialized.\n */\nexport function getI18n(): typeof i18n {\n if (!initialized) {\n initializeI18n();\n }\n return i18n;\n}\n\nexport { i18n };\nexport default i18n;\n","/**\n * SudobilityApp - Base app wrapper for all Sudobility apps\n *\n * Provides common infrastructure with sensible defaults:\n * - HelmetProvider for SEO\n * - I18nextProvider for localization\n * - ThemeProvider for theming (built-in default)\n * - NetworkProvider for online/offline status (built-in default)\n * - QueryClientProvider for TanStack Query (built-in default)\n * - ToastProvider for notifications (built-in default)\n * - BrowserRouter for routing\n * - InfoBanner for system notifications (built-in default)\n *\n * All providers have sensible defaults - only pass props to override.\n */\nimport {\n Suspense,\n ComponentType,\n ReactNode,\n useMemo,\n useRef,\n useEffect,\n} from 'react';\nimport { BrowserRouter } from 'react-router-dom';\nimport {\n QueryClient,\n QueryClientProvider,\n type QueryClient as QueryClientType,\n} from '@tanstack/react-query';\nimport { HelmetProvider } from 'react-helmet-async';\nimport { I18nextProvider } from 'react-i18next';\nimport type { i18n } from 'i18next';\nimport { useLocation } from 'react-router-dom';\nimport { NetworkProvider } from '@sudobility/devops-components';\nimport { getNetworkService, getFirebaseService } from '@sudobility/di/web';\nimport { InfoBanner } from '@sudobility/di_web';\nimport {\n ThemeProvider as SharedThemeProvider,\n Theme,\n FontSize,\n} from '@sudobility/components';\nimport {\n ToastProvider as SharedToastProvider,\n ToastContainer as SharedToastContainer,\n useToast,\n} from '@sudobility/components/ui/toast';\nimport { getI18n } from '../../i18n';\n\n/**\n * QueryClient type that's compatible across different package versions.\n * Uses structural typing to avoid cross-package type conflicts when using bun link.\n */\ntype QueryClientLike = {\n getDefaultOptions: () => unknown;\n getQueryCache: () => unknown;\n getMutationCache: () => unknown;\n};\n\n/**\n * i18n type that's compatible across different package versions.\n * Uses structural typing to avoid cross-package type conflicts when using bun link.\n */\ntype I18nLike = {\n language: string;\n languages: readonly string[];\n\n t: (...args: any[]) => any;\n};\n\nexport interface SudobilityAppProps {\n /** App routes and content */\n children: ReactNode;\n\n /**\n * i18next instance for localization (optional).\n * Defaults to built-in i18n that loads translations from /locales/.\n * Pass your own if you need custom configuration.\n */\n i18n?: I18nLike;\n\n /**\n * TanStack Query client instance (optional).\n * Defaults to a QueryClient with sensible settings.\n */\n queryClient?: QueryClientLike;\n\n /**\n * Custom ThemeProvider component (optional).\n * Defaults to SharedThemeProvider from @sudobility/components.\n */\n ThemeProvider?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Custom ToastProvider component (optional).\n * Defaults to ToastProvider from @sudobility/components.\n */\n ToastProvider?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Toast container component rendered after routes (optional).\n * Defaults to a built-in container that renders toasts from context at bottom-right.\n */\n ToastContainer?: ComponentType;\n\n /**\n * Whether to show the InfoBanner for notifications/errors.\n * Defaults to true. Set to false to disable.\n */\n showInfoBanner?: boolean;\n\n /** Custom loading fallback for Suspense (optional) */\n LoadingFallback?: ComponentType;\n\n /**\n * Custom NetworkProvider component (optional).\n * By default, uses the built-in NetworkProvider with getNetworkService().\n * Set to false to disable network status tracking entirely.\n */\n NetworkProvider?: ComponentType<{ children: ReactNode }> | false;\n\n /**\n * Page tracker component for analytics (optional).\n * By default, uses Firebase Analytics to track page_view events.\n * Pass a custom component to override, or false to disable.\n */\n PageTracker?: ComponentType | false;\n\n /**\n * Additional providers to wrap around the router content.\n * These are rendered inside ToastProvider but outside BrowserRouter.\n * Use this for app-specific providers like ApiProvider, SettingsProvider, etc.\n */\n AppProviders?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Storage key prefix for theme persistence (optional).\n * Defaults to \"sudobility\". Used for localStorage keys (e.g., \"sudobility-theme\").\n * Since localStorage is origin-scoped, apps on different domains don't need\n * different prefixes. Only override if running multiple app instances on the\n * same origin or if you want app-specific debugging visibility.\n */\n storageKeyPrefix?: string;\n\n /**\n * Custom router wrapper component (optional).\n * Defaults to BrowserRouter. Pass a fragment wrapper `({ children }) => <>{children}</>`\n * to skip the router entirely (useful when nesting inside an existing router).\n */\n RouterWrapper?: ComponentType<{ children: ReactNode }>;\n}\n\n/**\n * Default loading fallback component\n */\nfunction DefaultLoadingFallback() {\n return (\n <div className='min-h-screen flex items-center justify-center bg-theme-bg-primary'>\n <div className='animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600' />\n </div>\n );\n}\n\n/**\n * Default page tracker using Firebase Analytics.\n * Tracks page_view events on route changes.\n */\nfunction DefaultPageTracker(): null {\n const location = useLocation();\n const previousPathRef = useRef<string | null>(null);\n\n useEffect(() => {\n const currentPath = location.pathname;\n\n // Skip if same path\n if (previousPathRef.current === currentPath) {\n return;\n }\n\n previousPathRef.current = currentPath;\n\n // Track page view via Firebase Analytics\n try {\n const firebaseService = getFirebaseService();\n if (firebaseService?.analytics?.isSupported()) {\n firebaseService.analytics.logEvent('page_view', {\n page_path: currentPath,\n page_location: window.location.href,\n });\n }\n } catch {\n // Firebase not configured - silently skip tracking\n }\n }, [location.pathname]);\n\n return null;\n}\n\n/**\n * Default network provider that uses getNetworkService()\n */\nfunction DefaultNetworkProvider({ children }: { children: ReactNode }) {\n const networkService = useMemo(() => getNetworkService(), []);\n return (\n <NetworkProvider networkService={networkService}>\n {children}\n </NetworkProvider>\n );\n}\n\n/**\n * Create default QueryClient with sensible settings\n */\nfunction createDefaultQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000, // 5 minutes\n gcTime: 10 * 60 * 1000, // 10 minutes\n // Smart retry: don't retry on 4xx client errors, retry up to 3 times otherwise\n retry: (failureCount, error: unknown) => {\n const statusCode = (error as { statusCode?: number })?.statusCode;\n if (statusCode && statusCode >= 400 && statusCode < 500) {\n return false;\n }\n return failureCount < 3;\n },\n // Exponential backoff: 1s, 2s, 4s... up to 30s\n retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),\n refetchOnWindowFocus: true,\n },\n mutations: {\n retry: false,\n },\n },\n });\n}\n\n// Singleton default QueryClient\nlet defaultQueryClient: QueryClient | null = null;\nfunction getDefaultQueryClient(): QueryClient {\n if (!defaultQueryClient) {\n defaultQueryClient = createDefaultQueryClient();\n }\n return defaultQueryClient;\n}\n\n/**\n * Default theme provider using SharedThemeProvider from @sudobility/components\n */\nfunction createDefaultThemeProvider(storageKeyPrefix: string) {\n return function DefaultThemeProvider({ children }: { children: ReactNode }) {\n return (\n <SharedThemeProvider\n themeStorageKey={`${storageKeyPrefix}-theme`}\n fontSizeStorageKey={`${storageKeyPrefix}-font-size`}\n defaultTheme={Theme.LIGHT}\n defaultFontSize={FontSize.MEDIUM}\n >\n {children}\n </SharedThemeProvider>\n );\n };\n}\n\n/**\n * Default toast provider using SharedToastProvider from @sudobility/components\n */\nfunction DefaultToastProvider({ children }: { children: ReactNode }) {\n return <SharedToastProvider>{children}</SharedToastProvider>;\n}\n\n/**\n * Default toast container that renders toasts from context.\n * Wraps SharedToastContainer with useToast consumer.\n */\nfunction DefaultToastContainer() {\n const { toasts, removeToast } = useToast();\n\n if (toasts.length === 0) return null;\n\n return (\n <SharedToastContainer\n toasts={toasts}\n onDismiss={removeToast}\n position='bottom-right'\n />\n );\n}\n\n/**\n * SudobilityApp - Base app wrapper for all Sudobility apps\n *\n * @example\n * ```tsx\n * // Minimal usage - only i18n is required\n * import { SudobilityApp } from '@sudobility/building_blocks';\n * import i18n from './i18n';\n *\n * function App() {\n * return (\n * <SudobilityApp i18n={i18n}>\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityApp>\n * );\n * }\n *\n * // With custom providers\n * function App() {\n * return (\n * <SudobilityApp\n * i18n={i18n}\n * ThemeProvider={MyCustomThemeProvider}\n * AppProviders={ApiProvider}\n * storageKeyPrefix=\"myapp\"\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityApp>\n * );\n * }\n * ```\n */\nexport function SudobilityApp({\n children,\n i18n: i18nInstance,\n queryClient,\n ThemeProvider: ThemeProviderProp,\n ToastProvider: ToastProviderProp,\n ToastContainer,\n showInfoBanner = true,\n LoadingFallback = DefaultLoadingFallback,\n NetworkProvider: NetworkProviderProp,\n PageTracker: PageTrackerProp,\n AppProviders,\n storageKeyPrefix = 'sudobility',\n RouterWrapper,\n}: SudobilityAppProps) {\n // Get i18n instance (custom or default)\n const i18nToUse = i18nInstance ?? getI18n();\n\n // Determine which providers to use (custom or default)\n const NetworkProviderComponent =\n NetworkProviderProp === false\n ? null\n : (NetworkProviderProp ?? DefaultNetworkProvider);\n\n const PageTrackerComponent =\n PageTrackerProp === false ? null : (PageTrackerProp ?? DefaultPageTracker);\n\n const ThemeProviderComponent =\n ThemeProviderProp ?? createDefaultThemeProvider(storageKeyPrefix);\n\n const ToastProviderComponent = ToastProviderProp ?? DefaultToastProvider;\n\n const ToastContainerComponent = ToastContainer ?? DefaultToastContainer;\n\n const queryClientInstance = queryClient ?? getDefaultQueryClient();\n\n // Use custom RouterWrapper or default BrowserRouter\n const RouterWrapperComponent = RouterWrapper ?? BrowserRouter;\n\n // Build the router content\n let routerContent: ReactNode = (\n <>\n {PageTrackerComponent && <PageTrackerComponent />}\n <Suspense fallback={<LoadingFallback />}>{children}</Suspense>\n <ToastContainerComponent />\n {showInfoBanner && <InfoBanner />}\n </>\n );\n\n // Wrap with Router\n routerContent = (\n <RouterWrapperComponent>{routerContent}</RouterWrapperComponent>\n );\n\n // Wrap with AppProviders if provided\n if (AppProviders) {\n routerContent = <AppProviders>{routerContent}</AppProviders>;\n }\n\n // Wrap with ToastProvider\n routerContent = (\n <ToastProviderComponent>{routerContent}</ToastProviderComponent>\n );\n\n // Wrap with QueryClientProvider\n routerContent = (\n <QueryClientProvider client={queryClientInstance as QueryClientType}>\n {routerContent}\n </QueryClientProvider>\n );\n\n // Wrap with NetworkProvider (uses default if not explicitly disabled)\n if (NetworkProviderComponent) {\n routerContent = (\n <NetworkProviderComponent>{routerContent}</NetworkProviderComponent>\n );\n }\n\n // Wrap with ThemeProvider\n routerContent = (\n <ThemeProviderComponent>{routerContent}</ThemeProviderComponent>\n );\n\n // Wrap with I18nextProvider\n routerContent = (\n <I18nextProvider i18n={i18nToUse as i18n}>{routerContent}</I18nextProvider>\n );\n\n // Wrap with HelmetProvider\n return <HelmetProvider>{routerContent}</HelmetProvider>;\n}\n\nexport default SudobilityApp;\n","/**\n * Safe Subscription Context\n *\n * Provides a context that can be safely used even when subscription\n * provider isn't loaded yet. Returns stub values for unauthenticated users.\n */\n\nimport { createContext, useContext } from 'react';\nimport type { SubscriptionContextValue } from '@sudobility/subscription-components';\n\n/**\n * Stub subscription value for unauthenticated users or when\n * subscription provider hasn't loaded yet.\n */\nexport const STUB_SUBSCRIPTION_VALUE: SubscriptionContextValue = {\n products: [],\n currentSubscription: null,\n isLoading: false,\n error: null,\n initialize: () => Promise.resolve(),\n purchase: () => Promise.resolve(false),\n restore: () => Promise.resolve(false),\n refresh: () => Promise.resolve(),\n clearError: () => {},\n};\n\n/**\n * Context that provides subscription state with safe defaults.\n */\nexport const SafeSubscriptionContext = createContext<SubscriptionContextValue>(\n STUB_SUBSCRIPTION_VALUE\n);\n\n/**\n * Hook to safely access subscription context.\n * Returns stub values if provider isn't available.\n */\nexport function useSafeSubscription(): SubscriptionContextValue {\n return useContext(SafeSubscriptionContext);\n}\n"],"names":["SharedThemeProvider","SharedToastProvider","SharedToastContainer","ToastContainer"],"mappings":"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"}
|
package/dist/firebase.js
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import { jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { S as SudobilityApp, a as SafeSubscriptionContext, b as STUB_SUBSCRIPTION_VALUE } from "./SafeSubscriptionContext-
|
|
2
|
+
import { S as SudobilityApp, a as SafeSubscriptionContext, b as STUB_SUBSCRIPTION_VALUE } from "./SafeSubscriptionContext-BuMPCXVi.js";
|
|
3
3
|
import { useAuthStatus, AuthProvider } from "@sudobility/auth-components";
|
|
4
4
|
import { getFirebaseAuth, FirebaseAuthNetworkService, initializeFirebaseAuth, getFirebaseErrorMessage } from "@sudobility/auth_lib";
|
|
5
5
|
import { createContext, useState, useEffect, useCallback, useRef, useMemo, useContext, lazy, Suspense } from "react";
|
|
6
6
|
import { onIdTokenChanged } from "firebase/auth";
|
|
7
|
+
import { getInfoService } from "@sudobility/di/info";
|
|
7
8
|
import { InfoType } from "@sudobility/types";
|
|
8
9
|
import { EntityClient, CurrentEntityProvider, useCurrentEntity } from "@sudobility/entity_client";
|
|
9
10
|
import { SubscriptionProvider, useSubscriptionContext } from "@sudobility/subscription-components";
|
|
10
11
|
import { setRevenueCatUser, refreshSubscription, clearRevenueCatUser } from "@sudobility/subscription_lib";
|
|
11
12
|
import "@sudobility/components";
|
|
12
|
-
function getInfoService() {
|
|
13
|
-
{
|
|
14
|
-
throw new Error("Info service not initialized. Call initializeInfoService(implementation) at app startup.");
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
13
|
const ApiContext = createContext(null);
|
|
18
14
|
function useApi() {
|
|
19
15
|
const context = useContext(ApiContext);
|
package/dist/firebase.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebase.js","sources":["../node_modules/@sudobility/di/dist/info/info-singleton.js","../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":["let infoServiceInstance = null;\nexport function initializeInfoService(implementation) {\n if (infoServiceInstance) {\n return;\n }\n infoServiceInstance = implementation;\n}\nexport function getInfoService() {\n if (!infoServiceInstance) {\n throw new Error('Info service not initialized. Call initializeInfoService(implementation) at app startup.');\n }\n return infoServiceInstance;\n}\nexport function resetInfoService() {\n infoServiceInstance = null;\n}\n//# sourceMappingURL=info-singleton.js.map","/**\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 useRef,\n type ReactNode,\n} from 'react';\nimport { onIdTokenChanged } from 'firebase/auth';\nimport { getInfoService } from '@sudobility/di/info';\nimport { InfoType } from '@sudobility/types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport {\n getFirebaseAuth,\n FirebaseAuthNetworkService,\n} 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 // Listen for token changes - Firebase automatically refreshes tokens\n // onIdTokenChanged fires on: sign-in, sign-out, and token refresh\n useEffect(() => {\n if (!auth) {\n setToken(null);\n setTokenLoading(false);\n return;\n }\n\n setTokenLoading(true);\n\n const unsubscribe = onIdTokenChanged(auth, async firebaseUser => {\n if (!firebaseUser) {\n setToken(null);\n setTokenLoading(false);\n return;\n }\n\n try {\n const idToken = await firebaseUser.getIdToken();\n setToken(idToken);\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 setToken(null);\n } finally {\n setTokenLoading(false);\n }\n });\n\n return () => {\n unsubscribe();\n };\n }, [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 // Create FirebaseAuthNetworkService instance once\n // This handles automatic token refresh on 401 responses\n // Note: FirebaseAuthNetworkService doesn't perfectly implement NetworkClient interface\n // (different request signature), but it's compatible at runtime for our use cases.\n // Using type assertion as a workaround for the interface mismatch between\n // @sudobility/auth_lib and @sudobility/types.\n const networkClientRef = useRef<NetworkClient | null>(null);\n if (!networkClientRef.current) {\n const firebaseNetworkService = new FirebaseAuthNetworkService({\n onTokenRefreshFailed: error => {\n console.error('[ApiProvider] Token refresh failed:', error);\n try {\n getInfoService().show(\n 'Authentication Error',\n 'Session expired. Please sign in again.',\n InfoType.ERROR,\n 5000\n );\n } catch {\n // InfoService not available\n }\n },\n });\n // Type assertion needed due to interface mismatch between FirebaseAuthNetworkService\n // and NetworkClient (auth_lib uses RequestInit/Response while types uses NetworkRequestOptions/NetworkResponse)\n networkClientRef.current =\n firebaseNetworkService as unknown as NetworkClient;\n }\n\n const value = useMemo<ApiContextValue>(\n () => ({\n networkClient: networkClientRef.current!,\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 } from 'react';\nimport { SudobilityApp, SudobilityAppProps } from './SudobilityApp';\nimport { AuthProvider } from '@sudobility/auth-components';\nimport {\n getFirebaseAuth,\n getFirebaseErrorMessage,\n initializeFirebaseAuth,\n} from '@sudobility/auth_lib';\nimport { ApiProvider } from '../api';\n\n/** Auth text labels for UI - all fields required for localization */\nexport interface AuthTexts {\n signInTitle: string;\n signInWithEmail: string;\n createAccount: string;\n resetPassword: string;\n signIn: string;\n signUp: string;\n logout: string;\n login: string;\n continueWithGoogle: string;\n continueWithApple: string;\n continueWithEmail: string;\n sendResetLink: string;\n backToSignIn: string;\n close: string;\n email: string;\n password: string;\n confirmPassword: string;\n displayName: string;\n emailPlaceholder: string;\n passwordPlaceholder: string;\n confirmPasswordPlaceholder: string;\n displayNamePlaceholder: string;\n forgotPassword: string;\n noAccount: string;\n haveAccount: string;\n or: string;\n resetEmailSent: string;\n resetEmailSentDesc: string;\n passwordMismatch: string;\n passwordTooShort: string;\n loading: string;\n}\n\n/** Auth error messages for Firebase error codes */\nexport interface AuthErrorTexts {\n 'auth/user-not-found': string;\n 'auth/wrong-password': string;\n 'auth/invalid-email': string;\n 'auth/invalid-credential': string;\n 'auth/email-already-in-use': string;\n 'auth/weak-password': string;\n 'auth/too-many-requests': string;\n 'auth/network-request-failed': string;\n 'auth/popup-closed-by-user': string;\n 'auth/popup-blocked': string;\n 'auth/account-exists-with-different-credential': string;\n 'auth/operation-not-allowed': string;\n default: string;\n}\n\nexport interface SudobilityAppWithFirebaseAuthProps extends Omit<\n SudobilityAppProps,\n 'AppProviders' | 'RouterWrapper'\n> {\n /**\n * Custom Firebase auth provider wrapper component (optional).\n * If provided, authTexts and authErrorTexts are ignored.\n */\n AuthProviderWrapper?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Localized auth UI texts (required if not using custom AuthProviderWrapper).\n */\n authTexts?: AuthTexts;\n\n /**\n * Localized auth error messages (required if not using custom AuthProviderWrapper).\n */\n authErrorTexts?: AuthErrorTexts;\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 * Custom router wrapper component (optional).\n * Defaults to BrowserRouter. Pass a fragment wrapper `({ children }) => <>{children}</>`\n * to skip the router entirely (useful when nesting inside an existing router).\n */\n RouterWrapper?: 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 AuthProviderWrapper using Firebase\n */\nfunction DefaultAuthProviderWrapper({\n children,\n providers,\n enableAnonymous,\n texts,\n errorTexts,\n}: {\n children: ReactNode;\n providers: ('google' | 'email' | 'apple')[];\n enableAnonymous: boolean;\n texts: AuthTexts;\n errorTexts: AuthErrorTexts;\n}) {\n // Initialize Firebase Auth (idempotent - safe to call multiple times)\n initializeFirebaseAuth();\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 * // With custom AuthProviderWrapper (recommended for i18n)\n * import { SudobilityAppWithFirebaseAuth } from '@sudobility/building_blocks';\n *\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuth\n * AuthProviderWrapper={MyAuthProviderWrapper}\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuth>\n * );\n * }\n *\n * // With localized texts\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuth\n * authTexts={localizedAuthTexts}\n * authErrorTexts={localizedAuthErrorTexts}\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuth>\n * );\n * }\n * ```\n */\nexport function SudobilityAppWithFirebaseAuth({\n AuthProviderWrapper: AuthProviderWrapperProp,\n authTexts,\n authErrorTexts,\n authProviders = ['google', 'email'],\n enableAnonymousAuth = false,\n ApiProvider: ApiProviderProp,\n AppProviders,\n RouterWrapper,\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 // Require texts when using default AuthProviderWrapper\n if (!authTexts || !authErrorTexts) {\n throw new Error(\n '[SudobilityAppWithFirebaseAuth] authTexts and authErrorTexts are required when not using a custom AuthProviderWrapper'\n );\n }\n\n return (\n <DefaultAuthProviderWrapper\n providers={authProviders}\n enableAnonymous={enableAnonymousAuth}\n texts={authTexts}\n errorTexts={authErrorTexts}\n >\n {content}\n </DefaultAuthProviderWrapper>\n );\n };\n\n return (\n <SudobilityApp\n {...baseProps}\n AppProviders={CombinedProviders}\n RouterWrapper={RouterWrapper}\n />\n );\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/info';\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' | 'RouterWrapper'\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 * Custom router wrapper component (optional).\n * Defaults to BrowserRouter. Pass a fragment wrapper `({ children }) => <>{children}</>`\n * to skip the router entirely (useful when nesting inside an existing router).\n */\n RouterWrapper?: 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 RouterWrapper,\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 RouterWrapper={RouterWrapper}\n />\n );\n}\n\nexport default SudobilityAppWithFirebaseAuthAndEntities;\n"],"names":["SubscriptionProviderWrapper"],"mappings":"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","x_google_ignoreList":[0]}
|
|
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 useRef,\n type ReactNode,\n} from 'react';\nimport { onIdTokenChanged } from 'firebase/auth';\nimport { getInfoService } from '@sudobility/di/info';\nimport { InfoType } from '@sudobility/types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useAuthStatus } from '@sudobility/auth-components';\nimport {\n getFirebaseAuth,\n FirebaseAuthNetworkService,\n} 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 // Listen for token changes - Firebase automatically refreshes tokens\n // onIdTokenChanged fires on: sign-in, sign-out, and token refresh\n useEffect(() => {\n if (!auth) {\n setToken(null);\n setTokenLoading(false);\n return;\n }\n\n setTokenLoading(true);\n\n const unsubscribe = onIdTokenChanged(auth, async firebaseUser => {\n if (!firebaseUser) {\n setToken(null);\n setTokenLoading(false);\n return;\n }\n\n try {\n const idToken = await firebaseUser.getIdToken();\n setToken(idToken);\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 setToken(null);\n } finally {\n setTokenLoading(false);\n }\n });\n\n return () => {\n unsubscribe();\n };\n }, [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 // Create FirebaseAuthNetworkService instance once\n // This handles automatic token refresh on 401 responses\n // Note: FirebaseAuthNetworkService doesn't perfectly implement NetworkClient interface\n // (different request signature), but it's compatible at runtime for our use cases.\n // Using type assertion as a workaround for the interface mismatch between\n // @sudobility/auth_lib and @sudobility/types.\n const networkClientRef = useRef<NetworkClient | null>(null);\n if (!networkClientRef.current) {\n const firebaseNetworkService = new FirebaseAuthNetworkService({\n onTokenRefreshFailed: error => {\n console.error('[ApiProvider] Token refresh failed:', error);\n try {\n getInfoService().show(\n 'Authentication Error',\n 'Session expired. Please sign in again.',\n InfoType.ERROR,\n 5000\n );\n } catch {\n // InfoService not available\n }\n },\n });\n // Type assertion needed due to interface mismatch between FirebaseAuthNetworkService\n // and NetworkClient (auth_lib uses RequestInit/Response while types uses NetworkRequestOptions/NetworkResponse)\n networkClientRef.current =\n firebaseNetworkService as unknown as NetworkClient;\n }\n\n const value = useMemo<ApiContextValue>(\n () => ({\n networkClient: networkClientRef.current!,\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 } from 'react';\nimport { SudobilityApp, SudobilityAppProps } from './SudobilityApp';\nimport { AuthProvider } from '@sudobility/auth-components';\nimport {\n getFirebaseAuth,\n getFirebaseErrorMessage,\n initializeFirebaseAuth,\n} from '@sudobility/auth_lib';\nimport { ApiProvider } from '../api';\n\n/** Auth text labels for UI - all fields required for localization */\nexport interface AuthTexts {\n signInTitle: string;\n signInWithEmail: string;\n createAccount: string;\n resetPassword: string;\n signIn: string;\n signUp: string;\n logout: string;\n login: string;\n continueWithGoogle: string;\n continueWithApple: string;\n continueWithEmail: string;\n sendResetLink: string;\n backToSignIn: string;\n close: string;\n email: string;\n password: string;\n confirmPassword: string;\n displayName: string;\n emailPlaceholder: string;\n passwordPlaceholder: string;\n confirmPasswordPlaceholder: string;\n displayNamePlaceholder: string;\n forgotPassword: string;\n noAccount: string;\n haveAccount: string;\n or: string;\n resetEmailSent: string;\n resetEmailSentDesc: string;\n passwordMismatch: string;\n passwordTooShort: string;\n loading: string;\n}\n\n/** Auth error messages for Firebase error codes */\nexport interface AuthErrorTexts {\n 'auth/user-not-found': string;\n 'auth/wrong-password': string;\n 'auth/invalid-email': string;\n 'auth/invalid-credential': string;\n 'auth/email-already-in-use': string;\n 'auth/weak-password': string;\n 'auth/too-many-requests': string;\n 'auth/network-request-failed': string;\n 'auth/popup-closed-by-user': string;\n 'auth/popup-blocked': string;\n 'auth/account-exists-with-different-credential': string;\n 'auth/operation-not-allowed': string;\n default: string;\n}\n\nexport interface SudobilityAppWithFirebaseAuthProps extends Omit<\n SudobilityAppProps,\n 'AppProviders' | 'RouterWrapper'\n> {\n /**\n * Custom Firebase auth provider wrapper component (optional).\n * If provided, authTexts and authErrorTexts are ignored.\n */\n AuthProviderWrapper?: ComponentType<{ children: ReactNode }>;\n\n /**\n * Localized auth UI texts (required if not using custom AuthProviderWrapper).\n */\n authTexts?: AuthTexts;\n\n /**\n * Localized auth error messages (required if not using custom AuthProviderWrapper).\n */\n authErrorTexts?: AuthErrorTexts;\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 * Custom router wrapper component (optional).\n * Defaults to BrowserRouter. Pass a fragment wrapper `({ children }) => <>{children}</>`\n * to skip the router entirely (useful when nesting inside an existing router).\n */\n RouterWrapper?: 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 AuthProviderWrapper using Firebase\n */\nfunction DefaultAuthProviderWrapper({\n children,\n providers,\n enableAnonymous,\n texts,\n errorTexts,\n}: {\n children: ReactNode;\n providers: ('google' | 'email' | 'apple')[];\n enableAnonymous: boolean;\n texts: AuthTexts;\n errorTexts: AuthErrorTexts;\n}) {\n // Initialize Firebase Auth (idempotent - safe to call multiple times)\n initializeFirebaseAuth();\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 * // With custom AuthProviderWrapper (recommended for i18n)\n * import { SudobilityAppWithFirebaseAuth } from '@sudobility/building_blocks';\n *\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuth\n * AuthProviderWrapper={MyAuthProviderWrapper}\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuth>\n * );\n * }\n *\n * // With localized texts\n * function App() {\n * return (\n * <SudobilityAppWithFirebaseAuth\n * authTexts={localizedAuthTexts}\n * authErrorTexts={localizedAuthErrorTexts}\n * >\n * <Routes>\n * <Route path=\"/\" element={<HomePage />} />\n * </Routes>\n * </SudobilityAppWithFirebaseAuth>\n * );\n * }\n * ```\n */\nexport function SudobilityAppWithFirebaseAuth({\n AuthProviderWrapper: AuthProviderWrapperProp,\n authTexts,\n authErrorTexts,\n authProviders = ['google', 'email'],\n enableAnonymousAuth = false,\n ApiProvider: ApiProviderProp,\n AppProviders,\n RouterWrapper,\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 // Require texts when using default AuthProviderWrapper\n if (!authTexts || !authErrorTexts) {\n throw new Error(\n '[SudobilityAppWithFirebaseAuth] authTexts and authErrorTexts are required when not using a custom AuthProviderWrapper'\n );\n }\n\n return (\n <DefaultAuthProviderWrapper\n providers={authProviders}\n enableAnonymous={enableAnonymousAuth}\n texts={authTexts}\n errorTexts={authErrorTexts}\n >\n {content}\n </DefaultAuthProviderWrapper>\n );\n };\n\n return (\n <SudobilityApp\n {...baseProps}\n AppProviders={CombinedProviders}\n RouterWrapper={RouterWrapper}\n />\n );\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/info';\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' | 'RouterWrapper'\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 * Custom router wrapper component (optional).\n * Defaults to BrowserRouter. Pass a fragment wrapper `({ children }) => <>{children}</>`\n * to skip the router entirely (useful when nesting inside an existing router).\n */\n RouterWrapper?: 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 RouterWrapper,\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 RouterWrapper={RouterWrapper}\n />\n );\n}\n\nexport default SudobilityAppWithFirebaseAuthAndEntities;\n"],"names":["SubscriptionProviderWrapper"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import React, { useState, useRef, useMemo, useCallback, useEffect } from "react";
|
|
3
3
|
import { TopbarProvider, Topbar, TopbarLeft, TopbarNavigation, TopbarLogo, Logo, TopbarCenter, TopbarRight, TopbarActions, TopbarMobileContent, Footer, FooterCompact, FooterCompactLeft, FooterVersion, FooterCopyright, FooterCompactRight, FooterGrid, FooterLinkSection, FooterLink, FooterBottom, FooterBrand, FooterSocialLinks, LayoutProvider, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MasterListItem, Section, MasterDetailLayout } from "@sudobility/components";
|
|
4
4
|
import { clsx } from "clsx";
|
|
5
5
|
import { twMerge } from "tailwind-merge";
|
|
@@ -7,9 +7,10 @@ import { ChevronDownIcon, CalendarDaysIcon, PaintBrushIcon, LanguageIcon, Chevro
|
|
|
7
7
|
import { GRADIENT_CLASSES, textVariants } from "@sudobility/design";
|
|
8
8
|
import { cva } from "class-variance-authority";
|
|
9
9
|
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, GoogleAuthProvider, signInWithPopup } from "firebase/auth";
|
|
10
|
-
import { b, a, S,
|
|
10
|
+
import { b, a, S, g, i, u } from "./SafeSubscriptionContext-BuMPCXVi.js";
|
|
11
11
|
import { SubscriptionLayout, SubscriptionTile, SegmentedControl } from "@sudobility/subscription-components";
|
|
12
12
|
import { useSubscriptionPeriods, useSubscriptionForPeriod, useSubscribable, useUserSubscription, refreshSubscription } from "@sudobility/subscription_lib";
|
|
13
|
+
import { ToastContainer, ToastProvider, useToast } from "@sudobility/components/ui/toast";
|
|
13
14
|
import { default as default2 } from "i18next";
|
|
14
15
|
function cn(...inputs) {
|
|
15
16
|
return twMerge(clsx(inputs));
|
|
@@ -470,7 +471,7 @@ const ShareDropdown = ({
|
|
|
470
471
|
const [shareUrl, setShareUrl] = useState("");
|
|
471
472
|
const [isPreparingShare, setIsPreparingShare] = useState(false);
|
|
472
473
|
const [showCopiedFeedback, setShowCopiedFeedback] = useState(false);
|
|
473
|
-
|
|
474
|
+
React.useEffect(() => {
|
|
474
475
|
const onBeforeShare = shareConfig.onBeforeShare;
|
|
475
476
|
if (onBeforeShare && !shareUrl) {
|
|
476
477
|
const prepareUrl = async () => {
|
|
@@ -644,7 +645,7 @@ const AppBreadcrumbs = ({
|
|
|
644
645
|
return null;
|
|
645
646
|
}
|
|
646
647
|
return /* @__PURE__ */ jsx("div", { className: cn(breadcrumbContainerVariants({ variant }), className), children: /* @__PURE__ */ jsx("div", { className: cn("max-w-7xl mx-auto px-4 py-2", contentClassName), children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
647
|
-
/* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx("ol", { className: "flex items-center text-sm space-x-2", children: items.map((item, index) => /* @__PURE__ */ jsxs(
|
|
648
|
+
/* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx("ol", { className: "flex items-center text-sm space-x-2", children: items.map((item, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
648
649
|
/* @__PURE__ */ jsx("li", { children: item.current || !item.href ? /* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300", children: item.label }) : /* @__PURE__ */ jsx(
|
|
649
650
|
"a",
|
|
650
651
|
{
|
|
@@ -751,7 +752,7 @@ const AppFooter = ({
|
|
|
751
752
|
}
|
|
752
753
|
)
|
|
753
754
|
] }),
|
|
754
|
-
/* @__PURE__ */ jsx(FooterCompactRight, { children: links.map((link, index) => /* @__PURE__ */ jsx(
|
|
755
|
+
/* @__PURE__ */ jsx(FooterCompactRight, { children: links.map((link, index) => /* @__PURE__ */ jsx(React.Fragment, { children: link.onClick ? /* @__PURE__ */ jsx(
|
|
755
756
|
"button",
|
|
756
757
|
{
|
|
757
758
|
onClick: createTrackedLinkHandler(
|
|
@@ -1389,11 +1390,11 @@ const AppTextPage = ({
|
|
|
1389
1390
|
/* @__PURE__ */ jsx("h1", { className: "text-4xl font-bold text-gray-900 dark:text-gray-100 mb-8", children: text.title }),
|
|
1390
1391
|
/* @__PURE__ */ jsxs("div", { className: "prose prose-lg dark:prose-invert max-w-none", children: [
|
|
1391
1392
|
text.lastUpdated && /* @__PURE__ */ jsx(Paragraph, { className: "mb-6", children: text.lastUpdated.replace("{{date}}", lastUpdatedDate) }),
|
|
1392
|
-
text.sections.map((section, index) => /* @__PURE__ */ jsxs(
|
|
1393
|
+
text.sections.map((section, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
1393
1394
|
/* @__PURE__ */ jsx(SectionHeading, { children: section.title }),
|
|
1394
1395
|
isSectionWithSubsections(section) ? (
|
|
1395
1396
|
// Section with subsections (h3 + lists)
|
|
1396
|
-
/* @__PURE__ */ jsx(Fragment, { children: section.subsections.map((subsection, subIndex) => /* @__PURE__ */ jsxs(
|
|
1397
|
+
/* @__PURE__ */ jsx(Fragment, { children: section.subsections.map((subsection, subIndex) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
1397
1398
|
/* @__PURE__ */ jsx(SubsectionHeading, { children: subsection.title }),
|
|
1398
1399
|
/* @__PURE__ */ jsx(List, { items: subsection.items })
|
|
1399
1400
|
] }, subIndex)) })
|
|
@@ -2532,14 +2533,14 @@ export {
|
|
|
2532
2533
|
a as SafeSubscriptionContext,
|
|
2533
2534
|
S as SudobilityApp,
|
|
2534
2535
|
Theme,
|
|
2535
|
-
|
|
2536
|
-
|
|
2536
|
+
ToastContainer,
|
|
2537
|
+
ToastProvider,
|
|
2537
2538
|
cn,
|
|
2538
2539
|
g as getI18n,
|
|
2539
2540
|
default2 as i18n,
|
|
2540
2541
|
i as initializeI18n,
|
|
2541
2542
|
isRTL,
|
|
2542
2543
|
u as useSafeSubscription,
|
|
2543
|
-
|
|
2544
|
+
useToast
|
|
2544
2545
|
};
|
|
2545
2546
|
//# sourceMappingURL=index.js.map
|