@almadar/orb 2.47.0 → 3.0.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.
- package/package.json +6 -6
- package/shells/almadar-shell/package.json +1 -1
- package/shells/almadar-shell/packages/client/package.json +7 -7
- package/shells/almadar-shell/packages/client/src/config/firebase.ts +23 -2
- package/shells/almadar-shell/packages/client/src/features/auth/AuthContext.tsx +5 -0
- package/shells/almadar-shell/packages/client/src/features/auth/authService.ts +10 -7
- package/shells/almadar-shell/packages/client/src/features/auth/components/ProtectedRoute.tsx +9 -0
- package/shells/almadar-shell/packages/server/package.json +7 -7
- package/shells/almadar-shell/packages/server/src/index.ts +8 -2
- package/shells/almadar-shell/packages/shared/package.json +1 -1
- package/shells/almadar-shell/pnpm-lock.yaml +8989 -0
- package/shells/almadar-shell/tsup.config.ts +13 -0
- package/shells/almadar-shell/vitest.config.ts +8 -0
- package/shells/almadar-shell-hono/package.json +5 -4
- package/shells/almadar-shell-hono/packages/client/package.json +6 -6
- package/shells/almadar-shell-hono/packages/client/src/App.tsx +0 -3
- package/shells/almadar-shell-hono/packages/client/src/config/firebase.ts +23 -2
- package/shells/almadar-shell-hono/packages/client/src/features/auth/AuthContext.tsx +5 -0
- package/shells/almadar-shell-hono/packages/client/src/features/auth/authService.ts +10 -7
- package/shells/almadar-shell-hono/packages/client/src/features/auth/components/ProtectedRoute.tsx +9 -0
- package/shells/almadar-shell-hono/packages/server/package.json +5 -5
- package/shells/almadar-shell-hono/packages/server/src/index.ts +8 -2
- package/shells/almadar-shell-hono/pnpm-lock.yaml +9866 -0
- package/shells/almadar-shell-hono/tsup.config.ts +13 -0
- package/shells/almadar-shell-hono/vitest.config.ts +8 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@almadar/shell-hono",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Minimal full-stack shell template for Almadar applications (Hono server)",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
"packages/*"
|
|
7
7
|
],
|
|
8
8
|
"scripts": {
|
|
9
|
-
"dev": "concurrently -n client,server -c blue,green \"bun --filter @
|
|
10
|
-
"build": "
|
|
9
|
+
"dev": "concurrently -n client,server -c blue,green \"bun --filter @almadar/shell-hono-client dev\" \"bun --filter @almadar/shell-hono-server dev\"",
|
|
10
|
+
"build": "turbo run build",
|
|
11
11
|
"serve": "bun run packages/server/src/serve.ts",
|
|
12
12
|
"typecheck": "turbo run typecheck",
|
|
13
13
|
"lint": "turbo run lint",
|
|
@@ -32,5 +32,6 @@
|
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
|
35
|
-
}
|
|
35
|
+
},
|
|
36
|
+
"packageManager": "pnpm@10.30.3"
|
|
36
37
|
}
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
"test:watch": "vitest"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@almadar/ui": "
|
|
17
|
-
"@almadar/syntax": "
|
|
18
|
-
"@almadar/evaluator": "
|
|
19
|
-
"@almadar/patterns": "
|
|
20
|
-
"@almadar/core": "
|
|
16
|
+
"@almadar/ui": "^5.122.9",
|
|
17
|
+
"@almadar/syntax": "^1.11.0",
|
|
18
|
+
"@almadar/evaluator": "^2.31.0",
|
|
19
|
+
"@almadar/patterns": "^2.101.0",
|
|
20
|
+
"@almadar/core": "^10.31.0",
|
|
21
21
|
"firebase": "^11.4.0",
|
|
22
22
|
"@tanstack/react-query": "^5.67.3",
|
|
23
23
|
"react": "^19.0.0",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"postprocessing": "^6.39.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@almadar/eslint-plugin": "
|
|
45
|
+
"@almadar/eslint-plugin": "^2.14.0",
|
|
46
46
|
"@typescript-eslint/parser": "8.56.0",
|
|
47
47
|
"eslint": "10.0.0",
|
|
48
48
|
"@testing-library/react": "^16.1.0",
|
|
@@ -16,7 +16,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
|
16
16
|
import { ThemeProvider, UISlotProvider } from '@almadar/ui/context';
|
|
17
17
|
import { UISlotComponent, NotifyListener } from '@almadar/ui/components';
|
|
18
18
|
import {
|
|
19
|
-
EntityStoreProvider,
|
|
20
19
|
EventBusProvider,
|
|
21
20
|
VerificationProvider,
|
|
22
21
|
} from '@almadar/ui/providers';
|
|
@@ -49,7 +48,6 @@ function App() {
|
|
|
49
48
|
<I18nProvider value={i18nValue}>
|
|
50
49
|
<QueryClientProvider client={queryClient}>
|
|
51
50
|
<ThemeProvider>
|
|
52
|
-
<EntityStoreProvider>
|
|
53
51
|
<EventBusProvider>
|
|
54
52
|
<VerificationProvider>
|
|
55
53
|
<UISlotProvider>
|
|
@@ -74,7 +72,6 @@ function App() {
|
|
|
74
72
|
</UISlotProvider>
|
|
75
73
|
</VerificationProvider>
|
|
76
74
|
</EventBusProvider>
|
|
77
|
-
</EntityStoreProvider>
|
|
78
75
|
</ThemeProvider>
|
|
79
76
|
</QueryClientProvider>
|
|
80
77
|
</I18nProvider>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';
|
|
2
2
|
import { getAuth, Auth } from 'firebase/auth';
|
|
3
3
|
|
|
4
|
-
let app: FirebaseApp;
|
|
5
|
-
let auth: Auth;
|
|
4
|
+
let app: FirebaseApp | undefined;
|
|
5
|
+
let auth: Auth | undefined;
|
|
6
6
|
|
|
7
7
|
export async function initializeFirebase(): Promise<void> {
|
|
8
8
|
if (getApps().length > 0) {
|
|
@@ -30,8 +30,29 @@ export async function initializeFirebase(): Promise<void> {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// No credentials anywhere — auth stays disabled instead of crashing
|
|
34
|
+
// the app boot with auth/invalid-api-key.
|
|
35
|
+
if (!config.apiKey) {
|
|
36
|
+
console.warn('Firebase not configured — auth disabled (set VITE_APP_FIREBASE_* env vars)');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
app = initializeApp(config);
|
|
34
41
|
auth = getAuth(app);
|
|
35
42
|
}
|
|
36
43
|
|
|
44
|
+
export function requireAuth(): Auth {
|
|
45
|
+
if (!auth) {
|
|
46
|
+
throw new Error('Firebase auth is not configured — set VITE_APP_FIREBASE_* env vars');
|
|
47
|
+
}
|
|
48
|
+
return auth;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// True once Firebase initialized with real credentials. When false the
|
|
52
|
+
// app runs in auth-disabled mode: route guards pass through and sign-in
|
|
53
|
+
// actions report the missing config instead of crashing the boot.
|
|
54
|
+
export function isAuthEnabled(): boolean {
|
|
55
|
+
return auth !== undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
37
58
|
export { auth, app };
|
|
@@ -27,6 +27,11 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
|
|
27
27
|
let unsubscribe: (() => void) | undefined;
|
|
28
28
|
|
|
29
29
|
initializeFirebase().then(() => {
|
|
30
|
+
// Auth disabled (no Firebase config) — render without a listener.
|
|
31
|
+
if (!auth) {
|
|
32
|
+
setLoading(false);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
30
35
|
unsubscribe = auth.onAuthStateChanged((firebaseUser) => {
|
|
31
36
|
setUser(firebaseUser);
|
|
32
37
|
setLoading(false);
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
signInWithEmailLink as firebaseSignInWithEmailLink,
|
|
11
11
|
ActionCodeSettings,
|
|
12
12
|
} from 'firebase/auth';
|
|
13
|
-
import { auth } from '../../config/firebase';
|
|
13
|
+
import { auth, requireAuth } from '../../config/firebase';
|
|
14
14
|
|
|
15
15
|
const googleProvider = new GoogleAuthProvider();
|
|
16
16
|
|
|
@@ -18,7 +18,7 @@ export const authService = {
|
|
|
18
18
|
// Google Sign In
|
|
19
19
|
signInWithGoogle: async () => {
|
|
20
20
|
try {
|
|
21
|
-
const result = await signInWithPopup(
|
|
21
|
+
const result = await signInWithPopup(requireAuth(), googleProvider);
|
|
22
22
|
return result.user;
|
|
23
23
|
} catch (error) {
|
|
24
24
|
throw error;
|
|
@@ -28,7 +28,7 @@ export const authService = {
|
|
|
28
28
|
// Email/Password Sign In
|
|
29
29
|
signInWithEmail: async (email: string, password: string) => {
|
|
30
30
|
try {
|
|
31
|
-
const result = await signInWithEmailAndPassword(
|
|
31
|
+
const result = await signInWithEmailAndPassword(requireAuth(), email, password);
|
|
32
32
|
return result.user;
|
|
33
33
|
} catch (error) {
|
|
34
34
|
throw error;
|
|
@@ -38,7 +38,7 @@ export const authService = {
|
|
|
38
38
|
// Email/Password Sign Up
|
|
39
39
|
signUpWithEmail: async (email: string, password: string, displayName?: string) => {
|
|
40
40
|
try {
|
|
41
|
-
const result = await createUserWithEmailAndPassword(
|
|
41
|
+
const result = await createUserWithEmailAndPassword(requireAuth(), email, password);
|
|
42
42
|
|
|
43
43
|
if (displayName) {
|
|
44
44
|
await updateProfile(result.user, { displayName });
|
|
@@ -53,7 +53,7 @@ export const authService = {
|
|
|
53
53
|
// Sign Out
|
|
54
54
|
signOut: async () => {
|
|
55
55
|
try {
|
|
56
|
-
await firebaseSignOut(
|
|
56
|
+
await firebaseSignOut(requireAuth());
|
|
57
57
|
} catch (error) {
|
|
58
58
|
throw error;
|
|
59
59
|
}
|
|
@@ -62,19 +62,22 @@ export const authService = {
|
|
|
62
62
|
// Email Link Authentication
|
|
63
63
|
sendSignInLinkToEmail: async (email: string, actionCodeSettings: ActionCodeSettings) => {
|
|
64
64
|
try {
|
|
65
|
-
await firebaseSendSignInLinkToEmail(
|
|
65
|
+
await firebaseSendSignInLinkToEmail(requireAuth(), email, actionCodeSettings);
|
|
66
66
|
} catch (error) {
|
|
67
67
|
throw error;
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
|
|
71
71
|
isSignInWithEmailLink: (emailLink: string): boolean => {
|
|
72
|
+
// Predicate evaluated during Login render — degrade, don't throw,
|
|
73
|
+
// when auth is unconfigured: it simply isn't an email-link sign-in.
|
|
74
|
+
if (!auth) return false;
|
|
72
75
|
return firebaseIsSignInWithEmailLink(auth, emailLink);
|
|
73
76
|
},
|
|
74
77
|
|
|
75
78
|
signInWithEmailLink: async (email: string, emailLink: string) => {
|
|
76
79
|
try {
|
|
77
|
-
const result = await firebaseSignInWithEmailLink(
|
|
80
|
+
const result = await firebaseSignInWithEmailLink(requireAuth(), email, emailLink);
|
|
78
81
|
return result.user;
|
|
79
82
|
} catch (error) {
|
|
80
83
|
throw error;
|
package/shells/almadar-shell-hono/packages/client/src/features/auth/components/ProtectedRoute.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Navigate } from 'react-router-dom';
|
|
3
3
|
import { useAuthContext } from '../AuthContext';
|
|
4
|
+
import { isAuthEnabled } from '../../../config/firebase';
|
|
4
5
|
|
|
5
6
|
interface ProtectedRouteProps {
|
|
6
7
|
children: React.ReactNode;
|
|
@@ -17,6 +18,14 @@ const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => {
|
|
|
17
18
|
);
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
// Auth-disabled mode (no Firebase config): nothing can sign in, so
|
|
22
|
+
// gating would strand every route on /login — pass straight through.
|
|
23
|
+
// Checked after `loading` so a configured-but-initializing Firebase
|
|
24
|
+
// still shows the spinner instead of flashing protected content.
|
|
25
|
+
if (!isAuthEnabled()) {
|
|
26
|
+
return <>{children}</>;
|
|
27
|
+
}
|
|
28
|
+
|
|
20
29
|
if (!user) {
|
|
21
30
|
return <Navigate to="/login" replace />;
|
|
22
31
|
}
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"test:watch": "vitest"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@almadar/server-hono": "
|
|
17
|
-
"@almadar/evaluator": "
|
|
18
|
-
"@almadar/core": "
|
|
19
|
-
"@almadar/integrations": "
|
|
16
|
+
"@almadar/server-hono": "^0.4.8",
|
|
17
|
+
"@almadar/evaluator": "^2.31.0",
|
|
18
|
+
"@almadar/core": "^10.31.0",
|
|
19
|
+
"@almadar/integrations": "^2.17.0",
|
|
20
20
|
"hono": "^4.7.0",
|
|
21
21
|
"@hono/node-server": "^1.14.0",
|
|
22
22
|
"@hono/zod-validator": "^0.4.0",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"zod": "^3.22.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@almadar/eslint-plugin": "
|
|
28
|
+
"@almadar/eslint-plugin": "^2.14.0",
|
|
29
29
|
"@typescript-eslint/parser": "8.56.0",
|
|
30
30
|
"@types/bun": "^1.1.0",
|
|
31
31
|
"@types/node": "^20.17.19",
|
|
@@ -4,8 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import { initializeFirebase, env, logger } from '@almadar/server-hono';
|
|
6
6
|
|
|
7
|
-
// Initialize Firebase before anything else uses it
|
|
8
|
-
|
|
7
|
+
// Initialize Firebase before anything else uses it. When no credentials
|
|
8
|
+
// are configured (local dev without FIREBASE_*/FIRESTORE_EMULATOR_HOST),
|
|
9
|
+
// boot anyway — a purely client-side app must still serve.
|
|
10
|
+
try {
|
|
11
|
+
initializeFirebase();
|
|
12
|
+
} catch (e) {
|
|
13
|
+
logger.warn(`Firebase not configured — auth/db routes disabled: ${e instanceof Error ? e.message : String(e)}`);
|
|
14
|
+
}
|
|
9
15
|
|
|
10
16
|
import { serve } from '@hono/node-server';
|
|
11
17
|
import { app } from './app.js';
|