@marcwelti/mw-core 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +141 -0
  2. package/dist/context/index.d.mts +95 -0
  3. package/dist/context/index.d.ts +95 -0
  4. package/dist/context/index.js +280 -0
  5. package/dist/context/index.js.map +1 -0
  6. package/dist/context/index.mjs +276 -0
  7. package/dist/context/index.mjs.map +1 -0
  8. package/dist/firebase/index.d.mts +176 -0
  9. package/dist/firebase/index.d.ts +176 -0
  10. package/dist/firebase/index.js +393 -0
  11. package/dist/firebase/index.js.map +1 -0
  12. package/dist/firebase/index.mjs +318 -0
  13. package/dist/firebase/index.mjs.map +1 -0
  14. package/dist/hooks/index.d.mts +97 -0
  15. package/dist/hooks/index.d.ts +97 -0
  16. package/dist/hooks/index.js +618 -0
  17. package/dist/hooks/index.js.map +1 -0
  18. package/dist/hooks/index.mjs +611 -0
  19. package/dist/hooks/index.mjs.map +1 -0
  20. package/dist/index.d.mts +12 -0
  21. package/dist/index.d.ts +12 -0
  22. package/dist/index.js +922 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/index.mjs +843 -0
  25. package/dist/index.mjs.map +1 -0
  26. package/dist/server/index.d.mts +216 -0
  27. package/dist/server/index.d.ts +216 -0
  28. package/dist/server/index.js +309 -0
  29. package/dist/server/index.js.map +1 -0
  30. package/dist/server/index.mjs +276 -0
  31. package/dist/server/index.mjs.map +1 -0
  32. package/dist/storage-BU_rfYCi.d.mts +43 -0
  33. package/dist/storage-BU_rfYCi.d.ts +43 -0
  34. package/dist/types/index.d.mts +108 -0
  35. package/dist/types/index.d.ts +108 -0
  36. package/dist/types/index.js +12 -0
  37. package/dist/types/index.js.map +1 -0
  38. package/dist/types/index.mjs +3 -0
  39. package/dist/types/index.mjs.map +1 -0
  40. package/package.json +91 -0
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # @marcwelti/mw-core
2
+
3
+ Shared Firebase utilities, React hooks, and server-side authentication for Marc Welti applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @marcwelti/mw-core
9
+ ```
10
+
11
+ ## Features
12
+
13
+ ### Client-Side (Browser)
14
+
15
+ - **Firebase Auth**: Sign in with email/password, Google OAuth
16
+ - **Firestore**: Document and collection utilities
17
+ - **Storage**: File upload/download helpers
18
+ - **React Hooks**: `useAuth`, `useFirestore`, `useStorage`
19
+ - **Auth Context**: `AuthProvider` for React apps
20
+
21
+ ### Server-Side (Node.js / Next.js API Routes)
22
+
23
+ - **Firebase Admin SDK**: Server-side Firebase access
24
+ - **Session Cookies**: Cross-subdomain session management
25
+ - **Role Checking**: `hasRole()`, `isStaff()`, `isAdmin()`, `isOwner()`
26
+ - **Tier Checking**: `hasAccess()`, `hasSubscription()`
27
+ - **Middleware Helpers**: Auth verification for Next.js
28
+
29
+ ## Usage
30
+
31
+ ### Client-Side
32
+
33
+ ```typescript
34
+ // Firebase auth
35
+ import { signInWithEmail, signInWithGoogle, signOut } from '@marcwelti/mw-core/firebase';
36
+
37
+ // React hooks
38
+ import { useAuth } from '@marcwelti/mw-core/hooks';
39
+
40
+ // Auth context
41
+ import { AuthProvider, useAuthContext } from '@marcwelti/mw-core/context';
42
+ ```
43
+
44
+ ### Server-Side (API Routes)
45
+
46
+ ```typescript
47
+ import {
48
+ handleLogin,
49
+ handleLogout,
50
+ handleSession,
51
+ createSessionConfig
52
+ } from '@marcwelti/mw-core/server';
53
+
54
+ // In your API route
55
+ export async function POST(request: Request) {
56
+ const { idToken } = await request.json();
57
+ const sessionConfig = createSessionConfig('.marc-welti.ch');
58
+ const cookieHeader = await handleLogin(idToken, sessionConfig);
59
+
60
+ return new Response(JSON.stringify({ success: true }), {
61
+ headers: { 'Set-Cookie': cookieHeader }
62
+ });
63
+ }
64
+ ```
65
+
66
+ ### Role & Tier Checking
67
+
68
+ ```typescript
69
+ import { hasRole, isStaff, hasAccess } from '@marcwelti/mw-core/server';
70
+
71
+ // Check if user has required role
72
+ if (hasRole(user.role, ['owner', 'admin'])) {
73
+ // Grant access
74
+ }
75
+
76
+ // Check if user is staff (owner, admin, or employee)
77
+ if (isStaff(user.role)) {
78
+ // Show staff features
79
+ }
80
+
81
+ // Check subscription tier access
82
+ if (hasAccess(user.tier, ['MasterClass', 'PremiumOnlineKurs'])) {
83
+ // Show premium content
84
+ }
85
+ ```
86
+
87
+ ## Environment Variables
88
+
89
+ ### Client-Side (NEXT_PUBLIC_*)
90
+
91
+ ```bash
92
+ NEXT_PUBLIC_FIREBASE_API_KEY=
93
+ NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
94
+ NEXT_PUBLIC_FIREBASE_PROJECT_ID=
95
+ NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
96
+ NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
97
+ NEXT_PUBLIC_FIREBASE_APP_ID=
98
+ NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=
99
+ ```
100
+
101
+ ### Server-Side (Secret)
102
+
103
+ ```bash
104
+ FIREBASE_ADMIN_PROJECT_ID=
105
+ FIREBASE_ADMIN_CLIENT_EMAIL=
106
+ FIREBASE_ADMIN_PRIVATE_KEY=
107
+ ```
108
+
109
+ ## Subscription Tiers
110
+
111
+ - `MasterClass`
112
+ - `MasterClassLight`
113
+ - `PremiumOnlineKurs`
114
+ - `MasterOnlineKurs`
115
+ - `OnlineKursPro`
116
+ - `PremiumTraining`
117
+ - `VIPTraining`
118
+
119
+ ## User Roles
120
+
121
+ - `owner` - Marc only, full system access
122
+ - `employee` - Team members, limited to assigned customers
123
+ - `admin` - External developers/support, full technical access
124
+ - `user` - Customers, customer-facing features only
125
+
126
+ ## Development
127
+
128
+ ```bash
129
+ # Install dependencies
130
+ npm install
131
+
132
+ # Build
133
+ npm run build
134
+
135
+ # Watch mode
136
+ npm run dev
137
+ ```
138
+
139
+ ## License
140
+
141
+ UNLICENSED - Private package for Marc Welti applications only.
@@ -0,0 +1,95 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React, { ReactNode } from 'react';
3
+ import { User, UserCredential } from 'firebase/auth';
4
+
5
+ interface AuthContextValue {
6
+ /** The current authenticated user, or null if not authenticated */
7
+ user: User | null;
8
+ /** Whether the auth state is still loading */
9
+ loading: boolean;
10
+ /** Whether the user is authenticated */
11
+ isAuthenticated: boolean;
12
+ /** Any auth error that occurred */
13
+ error: Error | null;
14
+ /** Sign in with email and password */
15
+ signIn: (email: string, password: string) => Promise<UserCredential>;
16
+ /** Create a new account with email and password */
17
+ signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;
18
+ /** Sign out the current user */
19
+ signOut: () => Promise<void>;
20
+ /** Sign in with Google */
21
+ signInWithGoogle: () => Promise<UserCredential>;
22
+ /** Send a password reset email */
23
+ resetPassword: (email: string) => Promise<void>;
24
+ /** Update the user's profile */
25
+ updateProfile: (profile: {
26
+ displayName?: string;
27
+ photoURL?: string;
28
+ }) => Promise<void>;
29
+ /** Send email verification */
30
+ sendEmailVerification: () => Promise<void>;
31
+ /** Clear any auth errors */
32
+ clearError: () => void;
33
+ }
34
+ interface AuthProviderProps {
35
+ children: ReactNode;
36
+ /** Optional: Called when auth state changes */
37
+ onAuthStateChange?: (user: User | null) => void;
38
+ /** Optional: Loading component to show while auth state is loading */
39
+ loadingComponent?: ReactNode;
40
+ }
41
+ /**
42
+ * Auth Provider component that wraps your app and provides auth context
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * // app/layout.tsx
47
+ * import { AuthProvider } from '@marcwelti/mw-core';
48
+ *
49
+ * export default function RootLayout({ children }) {
50
+ * return (
51
+ * <html>
52
+ * <body>
53
+ * <AuthProvider>
54
+ * {children}
55
+ * </AuthProvider>
56
+ * </body>
57
+ * </html>
58
+ * );
59
+ * }
60
+ * ```
61
+ */
62
+ declare function AuthProvider({ children, onAuthStateChange, loadingComponent, }: AuthProviderProps): react_jsx_runtime.JSX.Element;
63
+ /**
64
+ * Hook to access auth context
65
+ * Must be used within an AuthProvider
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * function MyComponent() {
70
+ * const { user, signOut, isAuthenticated } = useAuthContext();
71
+ *
72
+ * if (!isAuthenticated) {
73
+ * return <LoginForm />;
74
+ * }
75
+ *
76
+ * return (
77
+ * <div>
78
+ * <p>Welcome, {user?.displayName}</p>
79
+ * <button onClick={signOut}>Sign Out</button>
80
+ * </div>
81
+ * );
82
+ * }
83
+ * ```
84
+ */
85
+ declare function useAuthContext(): AuthContextValue;
86
+ /**
87
+ * HOC for protecting routes that require authentication
88
+ * Redirects to login or shows loading state
89
+ */
90
+ declare function withAuth<P extends object>(WrappedComponent: React.ComponentType<P>, options?: {
91
+ LoadingComponent?: React.ComponentType;
92
+ UnauthenticatedComponent?: React.ComponentType;
93
+ }): (props: P) => react_jsx_runtime.JSX.Element;
94
+
95
+ export { type AuthContextValue, AuthProvider, type AuthProviderProps, useAuthContext, withAuth };
@@ -0,0 +1,95 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React, { ReactNode } from 'react';
3
+ import { User, UserCredential } from 'firebase/auth';
4
+
5
+ interface AuthContextValue {
6
+ /** The current authenticated user, or null if not authenticated */
7
+ user: User | null;
8
+ /** Whether the auth state is still loading */
9
+ loading: boolean;
10
+ /** Whether the user is authenticated */
11
+ isAuthenticated: boolean;
12
+ /** Any auth error that occurred */
13
+ error: Error | null;
14
+ /** Sign in with email and password */
15
+ signIn: (email: string, password: string) => Promise<UserCredential>;
16
+ /** Create a new account with email and password */
17
+ signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;
18
+ /** Sign out the current user */
19
+ signOut: () => Promise<void>;
20
+ /** Sign in with Google */
21
+ signInWithGoogle: () => Promise<UserCredential>;
22
+ /** Send a password reset email */
23
+ resetPassword: (email: string) => Promise<void>;
24
+ /** Update the user's profile */
25
+ updateProfile: (profile: {
26
+ displayName?: string;
27
+ photoURL?: string;
28
+ }) => Promise<void>;
29
+ /** Send email verification */
30
+ sendEmailVerification: () => Promise<void>;
31
+ /** Clear any auth errors */
32
+ clearError: () => void;
33
+ }
34
+ interface AuthProviderProps {
35
+ children: ReactNode;
36
+ /** Optional: Called when auth state changes */
37
+ onAuthStateChange?: (user: User | null) => void;
38
+ /** Optional: Loading component to show while auth state is loading */
39
+ loadingComponent?: ReactNode;
40
+ }
41
+ /**
42
+ * Auth Provider component that wraps your app and provides auth context
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * // app/layout.tsx
47
+ * import { AuthProvider } from '@marcwelti/mw-core';
48
+ *
49
+ * export default function RootLayout({ children }) {
50
+ * return (
51
+ * <html>
52
+ * <body>
53
+ * <AuthProvider>
54
+ * {children}
55
+ * </AuthProvider>
56
+ * </body>
57
+ * </html>
58
+ * );
59
+ * }
60
+ * ```
61
+ */
62
+ declare function AuthProvider({ children, onAuthStateChange, loadingComponent, }: AuthProviderProps): react_jsx_runtime.JSX.Element;
63
+ /**
64
+ * Hook to access auth context
65
+ * Must be used within an AuthProvider
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * function MyComponent() {
70
+ * const { user, signOut, isAuthenticated } = useAuthContext();
71
+ *
72
+ * if (!isAuthenticated) {
73
+ * return <LoginForm />;
74
+ * }
75
+ *
76
+ * return (
77
+ * <div>
78
+ * <p>Welcome, {user?.displayName}</p>
79
+ * <button onClick={signOut}>Sign Out</button>
80
+ * </div>
81
+ * );
82
+ * }
83
+ * ```
84
+ */
85
+ declare function useAuthContext(): AuthContextValue;
86
+ /**
87
+ * HOC for protecting routes that require authentication
88
+ * Redirects to login or shows loading state
89
+ */
90
+ declare function withAuth<P extends object>(WrappedComponent: React.ComponentType<P>, options?: {
91
+ LoadingComponent?: React.ComponentType;
92
+ UnauthenticatedComponent?: React.ComponentType;
93
+ }): (props: P) => react_jsx_runtime.JSX.Element;
94
+
95
+ export { type AuthContextValue, AuthProvider, type AuthProviderProps, useAuthContext, withAuth };
@@ -0,0 +1,280 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var auth = require('firebase/auth');
5
+ var app = require('firebase/app');
6
+ var firestore = require('firebase/firestore');
7
+ var storage = require('firebase/storage');
8
+ var jsxRuntime = require('react/jsx-runtime');
9
+
10
+ // src/context/AuthContext.tsx
11
+ function getFirebaseConfig() {
12
+ const config = {
13
+ apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || "",
14
+ authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || "",
15
+ projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || "",
16
+ storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || "",
17
+ messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || "",
18
+ appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || "",
19
+ measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID
20
+ };
21
+ const requiredFields = [
22
+ "apiKey",
23
+ "authDomain",
24
+ "projectId",
25
+ "storageBucket",
26
+ "messagingSenderId",
27
+ "appId"
28
+ ];
29
+ const missingFields = requiredFields.filter((field) => !config[field]);
30
+ if (missingFields.length > 0) {
31
+ console.warn(
32
+ `[mw-core] Missing Firebase config fields: ${missingFields.join(", ")}. Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`
33
+ );
34
+ }
35
+ return config;
36
+ }
37
+ function initializeFirebase(config) {
38
+ if (app.getApps().length > 0) {
39
+ return app.getApp();
40
+ }
41
+ const firebaseConfig = getFirebaseConfig();
42
+ return app.initializeApp(firebaseConfig);
43
+ }
44
+ var _app = null;
45
+ var _auth = null;
46
+ var _db = null;
47
+ var _storage = null;
48
+ function getFirebaseApp() {
49
+ if (!_app) {
50
+ _app = initializeFirebase();
51
+ }
52
+ return _app;
53
+ }
54
+ function getFirebaseAuth() {
55
+ if (!_auth) {
56
+ _auth = auth.getAuth(getFirebaseApp());
57
+ }
58
+ return _auth;
59
+ }
60
+ function getFirebaseFirestore() {
61
+ if (!_db) {
62
+ _db = firestore.getFirestore(getFirebaseApp());
63
+ }
64
+ return _db;
65
+ }
66
+ function getFirebaseStorage() {
67
+ if (!_storage) {
68
+ _storage = storage.getStorage(getFirebaseApp());
69
+ }
70
+ return _storage;
71
+ }
72
+ typeof window !== "undefined" ? getFirebaseApp() : null;
73
+ typeof window !== "undefined" ? getFirebaseAuth() : null;
74
+ typeof window !== "undefined" ? getFirebaseFirestore() : null;
75
+ typeof window !== "undefined" ? getFirebaseStorage() : null;
76
+
77
+ // src/firebase/auth.ts
78
+ async function signInWithEmail(email, password) {
79
+ const auth2 = getFirebaseAuth();
80
+ return auth.signInWithEmailAndPassword(auth2, email, password);
81
+ }
82
+ async function signUpWithEmail(email, password, displayName) {
83
+ const auth2 = getFirebaseAuth();
84
+ const credential = await auth.createUserWithEmailAndPassword(auth2, email, password);
85
+ if (displayName && credential.user) {
86
+ await auth.updateProfile(credential.user, { displayName });
87
+ }
88
+ return credential;
89
+ }
90
+ async function signOut() {
91
+ const auth2 = getFirebaseAuth();
92
+ return auth.signOut(auth2);
93
+ }
94
+ async function resetPassword(email) {
95
+ const auth2 = getFirebaseAuth();
96
+ return auth.sendPasswordResetEmail(auth2, email);
97
+ }
98
+ async function sendVerificationEmail(user) {
99
+ return auth.sendEmailVerification(user);
100
+ }
101
+ async function updateUserProfile(user, profile) {
102
+ return auth.updateProfile(user, profile);
103
+ }
104
+ async function signInWithGoogle() {
105
+ const auth2 = getFirebaseAuth();
106
+ const provider = new auth.GoogleAuthProvider();
107
+ provider.addScope("email");
108
+ provider.addScope("profile");
109
+ return auth.signInWithPopup(auth2, provider);
110
+ }
111
+ function subscribeToAuthState(callback) {
112
+ const auth2 = getFirebaseAuth();
113
+ return auth.onAuthStateChanged(auth2, callback);
114
+ }
115
+ var AuthContext = react.createContext(void 0);
116
+ function AuthProvider({
117
+ children,
118
+ onAuthStateChange,
119
+ loadingComponent
120
+ }) {
121
+ const [user, setUser] = react.useState(null);
122
+ const [loading, setLoading] = react.useState(true);
123
+ const [error, setError] = react.useState(null);
124
+ react.useEffect(() => {
125
+ const unsubscribe = subscribeToAuthState((user2) => {
126
+ setUser(user2);
127
+ setLoading(false);
128
+ onAuthStateChange?.(user2);
129
+ });
130
+ return unsubscribe;
131
+ }, [onAuthStateChange]);
132
+ const signIn = react.useCallback(async (email, password) => {
133
+ setError(null);
134
+ try {
135
+ return await signInWithEmail(email, password);
136
+ } catch (err) {
137
+ const error2 = err;
138
+ setError(error2);
139
+ throw error2;
140
+ }
141
+ }, []);
142
+ const signUp = react.useCallback(
143
+ async (email, password, displayName) => {
144
+ setError(null);
145
+ try {
146
+ return await signUpWithEmail(email, password, displayName);
147
+ } catch (err) {
148
+ const error2 = err;
149
+ setError(error2);
150
+ throw error2;
151
+ }
152
+ },
153
+ []
154
+ );
155
+ const signOut2 = react.useCallback(async () => {
156
+ setError(null);
157
+ try {
158
+ await signOut();
159
+ } catch (err) {
160
+ const error2 = err;
161
+ setError(error2);
162
+ throw error2;
163
+ }
164
+ }, []);
165
+ const signInWithGoogle2 = react.useCallback(async () => {
166
+ setError(null);
167
+ try {
168
+ return await signInWithGoogle();
169
+ } catch (err) {
170
+ const error2 = err;
171
+ setError(error2);
172
+ throw error2;
173
+ }
174
+ }, []);
175
+ const resetPassword2 = react.useCallback(async (email) => {
176
+ setError(null);
177
+ try {
178
+ await resetPassword(email);
179
+ } catch (err) {
180
+ const error2 = err;
181
+ setError(error2);
182
+ throw error2;
183
+ }
184
+ }, []);
185
+ const updateProfile2 = react.useCallback(
186
+ async (profile) => {
187
+ setError(null);
188
+ if (!user) {
189
+ const error2 = new Error("No authenticated user");
190
+ setError(error2);
191
+ throw error2;
192
+ }
193
+ try {
194
+ await updateUserProfile(user, profile);
195
+ } catch (err) {
196
+ const error2 = err;
197
+ setError(error2);
198
+ throw error2;
199
+ }
200
+ },
201
+ [user]
202
+ );
203
+ const sendEmailVerificationFn = react.useCallback(async () => {
204
+ setError(null);
205
+ if (!user) {
206
+ const error2 = new Error("No authenticated user");
207
+ setError(error2);
208
+ throw error2;
209
+ }
210
+ try {
211
+ await sendVerificationEmail(user);
212
+ } catch (err) {
213
+ const error2 = err;
214
+ setError(error2);
215
+ throw error2;
216
+ }
217
+ }, [user]);
218
+ const clearError = react.useCallback(() => {
219
+ setError(null);
220
+ }, []);
221
+ const value = react.useMemo(
222
+ () => ({
223
+ user,
224
+ loading,
225
+ isAuthenticated: !!user,
226
+ error,
227
+ signIn,
228
+ signUp,
229
+ signOut: signOut2,
230
+ signInWithGoogle: signInWithGoogle2,
231
+ resetPassword: resetPassword2,
232
+ updateProfile: updateProfile2,
233
+ sendEmailVerification: sendEmailVerificationFn,
234
+ clearError
235
+ }),
236
+ [
237
+ user,
238
+ loading,
239
+ error,
240
+ signIn,
241
+ signUp,
242
+ signOut2,
243
+ signInWithGoogle2,
244
+ resetPassword2,
245
+ updateProfile2,
246
+ sendEmailVerificationFn,
247
+ clearError
248
+ ]
249
+ );
250
+ if (loading && loadingComponent) {
251
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent });
252
+ }
253
+ return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value, children });
254
+ }
255
+ function useAuthContext() {
256
+ const context = react.useContext(AuthContext);
257
+ if (context === void 0) {
258
+ throw new Error("useAuthContext must be used within an AuthProvider");
259
+ }
260
+ return context;
261
+ }
262
+ function withAuth(WrappedComponent, options = {}) {
263
+ const { LoadingComponent, UnauthenticatedComponent } = options;
264
+ return function WithAuthComponent(props) {
265
+ const { user, loading, isAuthenticated } = useAuthContext();
266
+ if (loading) {
267
+ return LoadingComponent ? /* @__PURE__ */ jsxRuntime.jsx(LoadingComponent, {}) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Loading..." });
268
+ }
269
+ if (!isAuthenticated) {
270
+ return UnauthenticatedComponent ? /* @__PURE__ */ jsxRuntime.jsx(UnauthenticatedComponent, {}) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Please sign in to continue" });
271
+ }
272
+ return /* @__PURE__ */ jsxRuntime.jsx(WrappedComponent, { ...props });
273
+ };
274
+ }
275
+
276
+ exports.AuthProvider = AuthProvider;
277
+ exports.useAuthContext = useAuthContext;
278
+ exports.withAuth = withAuth;
279
+ //# sourceMappingURL=index.js.map
280
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/firebase/config.ts","../../src/firebase/auth.ts","../../src/context/AuthContext.tsx"],"names":["getApps","getApp","initializeApp","getAuth","getFirestore","getStorage","auth","signInWithEmailAndPassword","createUserWithEmailAndPassword","updateProfile","firebaseSignOut","sendPasswordResetEmail","sendEmailVerification","GoogleAuthProvider","signInWithPopup","onAuthStateChanged","createContext","useState","useEffect","user","useCallback","error","signOut","signInWithGoogle","resetPassword","useMemo","jsx","useContext"],"mappings":"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","file":"index.js","sourcesContent":["import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';\nimport { getAuth, Auth } from 'firebase/auth';\nimport { getFirestore, Firestore } from 'firebase/firestore';\nimport { getStorage, FirebaseStorage } from 'firebase/storage';\n\n/**\n * Firebase configuration interface\n * All values should be provided via environment variables\n */\nexport interface FirebaseConfig {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket: string;\n messagingSenderId: string;\n appId: string;\n measurementId?: string;\n}\n\n/**\n * Get Firebase configuration from environment variables\n * Works with Next.js NEXT_PUBLIC_ prefix\n */\nexport function getFirebaseConfig(): FirebaseConfig {\n const config: FirebaseConfig = {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || '',\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || '',\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || '',\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || '',\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '',\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,\n };\n\n // Validate required fields\n const requiredFields: (keyof FirebaseConfig)[] = [\n 'apiKey',\n 'authDomain',\n 'projectId',\n 'storageBucket',\n 'messagingSenderId',\n 'appId',\n ];\n\n const missingFields = requiredFields.filter((field) => !config[field]);\n\n if (missingFields.length > 0) {\n console.warn(\n `[mw-core] Missing Firebase config fields: ${missingFields.join(', ')}. ` +\n `Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`\n );\n }\n\n return config;\n}\n\n/**\n * Initialize Firebase app (singleton pattern)\n * Safe to call multiple times - returns existing instance if already initialized\n */\nexport function initializeFirebase(config?: FirebaseConfig): FirebaseApp {\n if (getApps().length > 0) {\n return getApp();\n }\n\n const firebaseConfig = config || getFirebaseConfig();\n return initializeApp(firebaseConfig);\n}\n\n// Lazy initialization singletons\nlet _app: FirebaseApp | null = null;\nlet _auth: Auth | null = null;\nlet _db: Firestore | null = null;\nlet _storage: FirebaseStorage | null = null;\n\n/**\n * Get the Firebase app instance\n */\nexport function getFirebaseApp(): FirebaseApp {\n if (!_app) {\n _app = initializeFirebase();\n }\n return _app;\n}\n\n/**\n * Get the Firebase Auth instance\n */\nexport function getFirebaseAuth(): Auth {\n if (!_auth) {\n _auth = getAuth(getFirebaseApp());\n }\n return _auth;\n}\n\n/**\n * Get the Firestore database instance\n */\nexport function getFirebaseFirestore(): Firestore {\n if (!_db) {\n _db = getFirestore(getFirebaseApp());\n }\n return _db;\n}\n\n/**\n * Get the Firebase Storage instance\n */\nexport function getFirebaseStorage(): FirebaseStorage {\n if (!_storage) {\n _storage = getStorage(getFirebaseApp());\n }\n return _storage;\n}\n\n// Convenience exports for direct access\nexport const app = typeof window !== 'undefined' ? getFirebaseApp() : null;\nexport const auth = typeof window !== 'undefined' ? getFirebaseAuth() : null;\nexport const db = typeof window !== 'undefined' ? getFirebaseFirestore() : null;\nexport const storage = typeof window !== 'undefined' ? getFirebaseStorage() : null;\n\n","import {\n signInWithEmailAndPassword,\n createUserWithEmailAndPassword,\n signOut as firebaseSignOut,\n sendPasswordResetEmail,\n sendEmailVerification,\n updateProfile,\n GoogleAuthProvider,\n signInWithPopup,\n signInWithRedirect,\n getRedirectResult,\n onAuthStateChanged,\n User,\n UserCredential,\n Auth,\n} from 'firebase/auth';\nimport { getFirebaseAuth } from './config';\n\n/**\n * Sign in with email and password\n */\nexport async function signInWithEmail(\n email: string,\n password: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n return signInWithEmailAndPassword(auth, email, password);\n}\n\n/**\n * Create a new account with email and password\n */\nexport async function signUpWithEmail(\n email: string,\n password: string,\n displayName?: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const credential = await createUserWithEmailAndPassword(auth, email, password);\n \n if (displayName && credential.user) {\n await updateProfile(credential.user, { displayName });\n }\n \n return credential;\n}\n\n/**\n * Sign out the current user\n */\nexport async function signOut(): Promise<void> {\n const auth = getFirebaseAuth();\n return firebaseSignOut(auth);\n}\n\n/**\n * Send a password reset email\n */\nexport async function resetPassword(email: string): Promise<void> {\n const auth = getFirebaseAuth();\n return sendPasswordResetEmail(auth, email);\n}\n\n/**\n * Send email verification to the current user\n */\nexport async function sendVerificationEmail(user: User): Promise<void> {\n return sendEmailVerification(user);\n}\n\n/**\n * Update the current user's profile\n */\nexport async function updateUserProfile(\n user: User,\n profile: { displayName?: string; photoURL?: string }\n): Promise<void> {\n return updateProfile(user, profile);\n}\n\n/**\n * Sign in with Google using popup\n */\nexport async function signInWithGoogle(): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithPopup(auth, provider);\n}\n\n/**\n * Sign in with Google using redirect (better for mobile)\n */\nexport async function signInWithGoogleRedirect(): Promise<void> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithRedirect(auth, provider);\n}\n\n/**\n * Get the result of a redirect sign-in\n */\nexport async function getGoogleRedirectResult(): Promise<UserCredential | null> {\n const auth = getFirebaseAuth();\n return getRedirectResult(auth);\n}\n\n/**\n * Subscribe to auth state changes\n */\nexport function subscribeToAuthState(\n callback: (user: User | null) => void\n): () => void {\n const auth = getFirebaseAuth();\n return onAuthStateChanged(auth, callback);\n}\n\n/**\n * Get the current user (may be null if not authenticated)\n */\nexport function getCurrentUser(): User | null {\n const auth = getFirebaseAuth();\n return auth.currentUser;\n}\n\n/**\n * Wait for the auth state to be determined\n * Useful for SSR/initial load\n */\nexport function waitForAuthState(): Promise<User | null> {\n return new Promise((resolve) => {\n const auth = getFirebaseAuth();\n const unsubscribe = onAuthStateChanged(auth, (user) => {\n unsubscribe();\n resolve(user);\n });\n });\n}\n\n// Re-export useful types\nexport type { User, UserCredential, Auth };\n\n","'use client';\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useMemo,\n ReactNode,\n} from 'react';\nimport { User, UserCredential } from 'firebase/auth';\nimport {\n subscribeToAuthState,\n signInWithEmail,\n signUpWithEmail,\n signOut as firebaseSignOut,\n signInWithGoogle as firebaseSignInWithGoogle,\n resetPassword as firebaseResetPassword,\n updateUserProfile,\n sendVerificationEmail,\n} from '../firebase/auth';\n\nexport interface AuthContextValue {\n /** The current authenticated user, or null if not authenticated */\n user: User | null;\n /** Whether the auth state is still loading */\n loading: boolean;\n /** Whether the user is authenticated */\n isAuthenticated: boolean;\n /** Any auth error that occurred */\n error: Error | null;\n /** Sign in with email and password */\n signIn: (email: string, password: string) => Promise<UserCredential>;\n /** Create a new account with email and password */\n signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;\n /** Sign out the current user */\n signOut: () => Promise<void>;\n /** Sign in with Google */\n signInWithGoogle: () => Promise<UserCredential>;\n /** Send a password reset email */\n resetPassword: (email: string) => Promise<void>;\n /** Update the user's profile */\n updateProfile: (profile: { displayName?: string; photoURL?: string }) => Promise<void>;\n /** Send email verification */\n sendEmailVerification: () => Promise<void>;\n /** Clear any auth errors */\n clearError: () => void;\n}\n\nconst AuthContext = createContext<AuthContextValue | undefined>(undefined);\n\nexport interface AuthProviderProps {\n children: ReactNode;\n /** Optional: Called when auth state changes */\n onAuthStateChange?: (user: User | null) => void;\n /** Optional: Loading component to show while auth state is loading */\n loadingComponent?: ReactNode;\n}\n\n/**\n * Auth Provider component that wraps your app and provides auth context\n * \n * @example\n * ```tsx\n * // app/layout.tsx\n * import { AuthProvider } from '@marcwelti/mw-core';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <AuthProvider>\n * {children}\n * </AuthProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function AuthProvider({\n children,\n onAuthStateChange,\n loadingComponent,\n}: AuthProviderProps) {\n const [user, setUser] = useState<User | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n const unsubscribe = subscribeToAuthState((user) => {\n setUser(user);\n setLoading(false);\n onAuthStateChange?.(user);\n });\n\n return unsubscribe;\n }, [onAuthStateChange]);\n\n const signIn = useCallback(async (email: string, password: string) => {\n setError(null);\n try {\n return await signInWithEmail(email, password);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signUp = useCallback(\n async (email: string, password: string, displayName?: string) => {\n setError(null);\n try {\n return await signUpWithEmail(email, password, displayName);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n []\n );\n\n const signOut = useCallback(async () => {\n setError(null);\n try {\n await firebaseSignOut();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signInWithGoogle = useCallback(async () => {\n setError(null);\n try {\n return await firebaseSignInWithGoogle();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const resetPassword = useCallback(async (email: string) => {\n setError(null);\n try {\n await firebaseResetPassword(email);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const updateProfile = useCallback(\n async (profile: { displayName?: string; photoURL?: string }) => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await updateUserProfile(user, profile);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n [user]\n );\n\n const sendEmailVerificationFn = useCallback(async () => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await sendVerificationEmail(user);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, [user]);\n\n const clearError = useCallback(() => {\n setError(null);\n }, []);\n\n const value = useMemo<AuthContextValue>(\n () => ({\n user,\n loading,\n isAuthenticated: !!user,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerification: sendEmailVerificationFn,\n clearError,\n }),\n [\n user,\n loading,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerificationFn,\n clearError,\n ]\n );\n\n // Show loading component while determining auth state\n if (loading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Hook to access auth context\n * Must be used within an AuthProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, signOut, isAuthenticated } = useAuthContext();\n * \n * if (!isAuthenticated) {\n * return <LoginForm />;\n * }\n * \n * return (\n * <div>\n * <p>Welcome, {user?.displayName}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useAuthContext(): AuthContextValue {\n const context = useContext(AuthContext);\n \n if (context === undefined) {\n throw new Error('useAuthContext must be used within an AuthProvider');\n }\n \n return context;\n}\n\n/**\n * HOC for protecting routes that require authentication\n * Redirects to login or shows loading state\n */\nexport function withAuth<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n options: {\n LoadingComponent?: React.ComponentType;\n UnauthenticatedComponent?: React.ComponentType;\n } = {}\n) {\n const { LoadingComponent, UnauthenticatedComponent } = options;\n\n return function WithAuthComponent(props: P) {\n const { user, loading, isAuthenticated } = useAuthContext();\n\n if (loading) {\n return LoadingComponent ? <LoadingComponent /> : <div>Loading...</div>;\n }\n\n if (!isAuthenticated) {\n return UnauthenticatedComponent ? (\n <UnauthenticatedComponent />\n ) : (\n <div>Please sign in to continue</div>\n );\n }\n\n return <WrappedComponent {...props} />;\n };\n}\n\n"]}