@almadar/orb 2.47.1 → 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.
Files changed (25) hide show
  1. package/package.json +6 -6
  2. package/shells/almadar-shell/package.json +1 -1
  3. package/shells/almadar-shell/packages/client/package.json +7 -7
  4. package/shells/almadar-shell/packages/client/src/config/firebase.ts +23 -2
  5. package/shells/almadar-shell/packages/client/src/features/auth/AuthContext.tsx +5 -0
  6. package/shells/almadar-shell/packages/client/src/features/auth/authService.ts +10 -7
  7. package/shells/almadar-shell/packages/client/src/features/auth/components/ProtectedRoute.tsx +9 -0
  8. package/shells/almadar-shell/packages/server/package.json +7 -7
  9. package/shells/almadar-shell/packages/server/src/index.ts +8 -2
  10. package/shells/almadar-shell/packages/shared/package.json +1 -1
  11. package/shells/almadar-shell/pnpm-lock.yaml +8989 -0
  12. package/shells/almadar-shell/tsup.config.ts +13 -0
  13. package/shells/almadar-shell/vitest.config.ts +8 -0
  14. package/shells/almadar-shell-hono/package.json +5 -4
  15. package/shells/almadar-shell-hono/packages/client/package.json +6 -6
  16. package/shells/almadar-shell-hono/packages/client/src/App.tsx +0 -3
  17. package/shells/almadar-shell-hono/packages/client/src/config/firebase.ts +23 -2
  18. package/shells/almadar-shell-hono/packages/client/src/features/auth/AuthContext.tsx +5 -0
  19. package/shells/almadar-shell-hono/packages/client/src/features/auth/authService.ts +10 -7
  20. package/shells/almadar-shell-hono/packages/client/src/features/auth/components/ProtectedRoute.tsx +9 -0
  21. package/shells/almadar-shell-hono/packages/server/package.json +5 -5
  22. package/shells/almadar-shell-hono/packages/server/src/index.ts +8 -2
  23. package/shells/almadar-shell-hono/pnpm-lock.yaml +9866 -0
  24. package/shells/almadar-shell-hono/tsup.config.ts +13 -0
  25. package/shells/almadar-shell-hono/vitest.config.ts +8 -0
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['esm'],
6
+ dts: true,
7
+ clean: true,
8
+ sourcemap: false,
9
+ minify: true,
10
+ treeshake: true,
11
+ splitting: false,
12
+ outDir: 'dist',
13
+ });
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ include: ['__tests__/**/*.test.ts', 'test/**/*.test.ts'],
6
+ globals: true,
7
+ },
8
+ });
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@almadar/shell-hono",
3
- "version": "0.1.3",
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 @app/client dev\" \"bun --filter @app/server dev\"",
10
- "build": "bun run --filter @app/shared build && bun run --filter @app/client build && bun run --filter @app/server 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": ">=4.30.0",
17
- "@almadar/syntax": ">=1.4.0",
18
- "@almadar/evaluator": ">=2.9.2",
19
- "@almadar/patterns": ">=2.18.1",
20
- "@almadar/core": ">=5.8.0",
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": ">=2.8.0",
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(auth, googleProvider);
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(auth, email, password);
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(auth, email, password);
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(auth);
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(auth, email, actionCodeSettings);
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(auth, email, emailLink);
80
+ const result = await firebaseSignInWithEmailLink(requireAuth(), email, emailLink);
78
81
  return result.user;
79
82
  } catch (error) {
80
83
  throw error;
@@ -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": ">=0.2.0",
17
- "@almadar/evaluator": ">=2.9.2",
18
- "@almadar/core": ">=5.8.0",
19
- "@almadar/integrations": ">=2.6.1",
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": ">=2.8.0",
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
- initializeFirebase();
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';