@rebasepro/client-firebase 0.2.3 → 0.2.5

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.
@@ -1,5 +1,5 @@
1
1
  import { ApplicationVerifier, ConfirmationResult, User as FirebaseUser } from "@firebase/auth";
2
- import { AuthController, Role, User } from "@rebasepro/types";
2
+ import type { AuthController, User } from "@rebasepro/types";
3
3
  /**
4
4
  * @group Firebase
5
5
  */
@@ -37,5 +37,5 @@ export type FirebaseAuthController<USER extends User = FirebaseUserWrapper, Extr
37
37
  */
38
38
  skipLogin: () => void;
39
39
  setUser: (user: USER | null) => void;
40
- setUserRoles: (roles: Role[]) => void;
40
+ setUserRoles: (roles: string[]) => void;
41
41
  };
@@ -6,4 +6,10 @@
6
6
  * @param query
7
7
  * @group Firebase
8
8
  */
9
- export declare function performAlgoliaTextSearch(client: any, indexName: string, query: string): Promise<readonly string[]>;
9
+ export declare function performAlgoliaTextSearch(client: {
10
+ searchSingleIndex: (params: Record<string, unknown>) => Promise<{
11
+ hits: Array<{
12
+ objectID: string;
13
+ }>;
14
+ }>;
15
+ }, indexName: string, query: string): Promise<readonly string[]>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/client-firebase",
3
3
  "type": "module",
4
- "version": "0.2.3",
4
+ "version": "0.2.5",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -14,12 +14,12 @@
14
14
  "fast-equals": "6.0.0",
15
15
  "fuse.js": "^7.3.0",
16
16
  "react-router-dom": "^7.15.1",
17
- "@rebasepro/admin": "0.2.3",
18
- "@rebasepro/common": "0.2.3",
19
- "@rebasepro/types": "0.2.3",
20
- "@rebasepro/ui": "0.2.3",
21
- "@rebasepro/utils": "0.2.3",
22
- "@rebasepro/core": "0.2.3"
17
+ "@rebasepro/admin": "0.2.5",
18
+ "@rebasepro/core": "0.2.5",
19
+ "@rebasepro/common": "0.2.5",
20
+ "@rebasepro/ui": "0.2.5",
21
+ "@rebasepro/utils": "0.2.5",
22
+ "@rebasepro/types": "0.2.5"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "firebase": "^10.12.2 || ^11.0.0 || ^12.0.0",
@@ -261,7 +261,7 @@ export function FirebaseLoginView({
261
261
  } else if (notAllowedError instanceof Error) {
262
262
  notAllowedMessage = notAllowedError.message;
263
263
  } else {
264
- notAllowedMessage = "It looks like you don't have access to the CMS, based on the specified Authenticator configuration";
264
+ notAllowedMessage = "It looks like you don't have access to the CMS, based on the specified access gate configuration";
265
265
  }
266
266
  }
267
267
 
@@ -10,7 +10,6 @@ import {
10
10
  useBuildModeController,
11
11
  useBuildAdminModeController,
12
12
  AdminModeControllerProvider,
13
- useValidateAuthenticator,
14
13
  RebaseRoutes
15
14
  } from "@rebasepro/core";
16
15
  import {
@@ -27,7 +26,7 @@ import {
27
26
  SideEntityProvider,
28
27
  RebaseRoute
29
28
  } from "@rebasepro/admin";
30
- import { Entity, PropertyConfig, UserManagementDelegate } from "@rebasepro/types";
29
+ import { Entity, PropertyConfig, RebaseContext, UserManagementDelegate } from "@rebasepro/types";
31
30
  import { CenteredView, CircularProgressCenter } from "@rebasepro/ui";
32
31
  import { buildRebaseData } from "@rebasepro/common";
33
32
  import { Route, Outlet, Navigate } from "react-router-dom";
@@ -40,7 +39,8 @@ import {
40
39
  useFirebaseStorageSource,
41
40
  useFirestoreDriver,
42
41
  useInitialiseFirebase,
43
- useBuildUserManagement
42
+ useBuildUserManagement,
43
+ useFirebaseAccessGate
44
44
  } from "../hooks";
45
45
 
46
46
  import { FirebaseAuthController } from "../types";
@@ -70,7 +70,7 @@ export function RebaseFirebaseApp({
70
70
  name,
71
71
  logo,
72
72
  logoDark,
73
- authenticator,
73
+ accessGate,
74
74
  collections,
75
75
  views,
76
76
  adminViews,
@@ -158,15 +158,15 @@ export function RebaseFirebaseApp({
158
158
  });
159
159
 
160
160
  /**
161
- * Validate authenticator
161
+ * Validate access gate
162
162
  */
163
163
  const {
164
164
  authLoading,
165
165
  canAccessMainView,
166
166
  notAllowedError
167
- } = useValidateAuthenticator({
167
+ } = useFirebaseAccessGate({
168
168
  authController,
169
- authenticator,
169
+ accessGate,
170
170
  data: buildRebaseData(firestoreDelegate),
171
171
  storageSource
172
172
  });
@@ -234,7 +234,7 @@ export function RebaseFirebaseApp({
234
234
  {({
235
235
  context,
236
236
  loading
237
- }: { context: any, loading: boolean }) => {
237
+ }: { context: RebaseContext, loading: boolean }) => {
238
238
 
239
239
  let component;
240
240
  if (loading || authLoading) {
@@ -250,7 +250,7 @@ export function RebaseFirebaseApp({
250
250
  signInOptions={signInOptions ?? DEFAULT_SIGN_IN_OPTIONS}
251
251
  firebaseApp={firebaseApp}
252
252
  authController={authController}
253
- notAllowedError={notAllowedError}/>
253
+ notAllowedError={notAllowedError instanceof Error ? notAllowedError : typeof notAllowedError === "string" ? notAllowedError : undefined}/>
254
254
  );
255
255
  } else {
256
256
  const firstCollectionEntry = navigationStateController.topLevelNavigation?.navigationEntries.find(e => e.type === "collection");
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
 
3
- import { Authenticator, AnalyticsEvent, AppView, AppViewsBuilder, EntityCollection, EntityCollectionsBuilder, RebasePlugin, Locale, PropertyConfig } from "@rebasepro/types";
3
+ import { AnalyticsEvent, AppView, AppViewsBuilder, EntityCollection, EntityCollectionsBuilder, RebasePlugin, Locale, PropertyConfig } from "@rebasepro/types";
4
+ import { FirebaseAccessGate } from "../hooks/useFirebaseAccessGate";
4
5
  import { UserManagementDelegate } from "@rebasepro/types";
5
6
  import { FirebaseApp } from "@firebase/app";
6
7
  import { FirebaseLoginViewProps } from "./FirebaseLoginView";
@@ -64,12 +65,12 @@ export type RebaseFirebaseAppProps = {
64
65
 
65
66
  /**
66
67
  * Do the users need to log in to access the CMS.
67
- * You can specify an Authenticator function to discriminate which users can
68
- * access the CMS or not.
68
+ * You can specify a {@link FirebaseAccessGate} function to discriminate
69
+ * which users can access the CMS or not.
69
70
  * If not specified, authentication is enabled but no user restrictions
70
71
  * apply
71
72
  */
72
- authenticator?: boolean | Authenticator<FirebaseUserWrapper>;
73
+ accessGate?: boolean | FirebaseAccessGate<FirebaseUserWrapper>;
73
74
 
74
75
  /**
75
76
  * List of sign in options that will be displayed in the login
@@ -6,3 +6,4 @@ export * from "./useFirestoreDriver";
6
6
  export * from "./useFirebaseRealTimeDBDelegate";
7
7
  export * from "./useRecaptcha";
8
8
  export * from "./useBuildUserManagement";
9
+ export * from "./useFirebaseAccessGate";
@@ -15,7 +15,7 @@ export interface InitializeAppCheckProps {
15
15
  export interface InitializeAppCheckResult {
16
16
  loading: boolean;
17
17
  appCheckVerified?: boolean;
18
- error?: any;
18
+ error?: unknown;
19
19
  }
20
20
 
21
21
  /**
@@ -35,7 +35,7 @@ export function useAppCheck({
35
35
 
36
36
  const [appCheckLoading, setAppCheckLoading] = React.useState<boolean>(false);
37
37
  const [appCheckVerified, setAppCheckVerified] = React.useState<boolean | undefined>(undefined);
38
- const [error, setError] = React.useState<any>();
38
+ const [error, setError] = React.useState<unknown>();
39
39
 
40
40
  const initialCheck = useRef<boolean>(false);
41
41
 
@@ -51,9 +51,9 @@ export function useAppCheck({
51
51
  setAppCheckVerified(true);
52
52
  console.debug("App Check success.");
53
53
  }
54
- } catch (e: any) {
54
+ } catch (e: unknown) {
55
55
  console.error("App Check error:", e);
56
- setError(e.message);
56
+ setError(e instanceof Error ? e.message : String(e));
57
57
  }
58
58
  }, [options?.forceRefresh]);
59
59
 
@@ -4,17 +4,22 @@ import { deepEqual as equal } from "fast-equals";
4
4
  import { removeUndefined } from "@rebasepro/utils";
5
5
  import {
6
6
  AuthController,
7
- Authenticator,
8
7
  DataDriver,
9
8
  Entity,
10
- Role,
11
9
  User,
12
10
  UserManagementDelegate
13
11
  } from "@rebasepro/types";
12
+ import { FirebaseAccessGate } from "./useFirebaseAccessGate";
14
13
 
15
- type UserWithRoleIds<USER extends User = any> = Omit<USER, "roles"> & { roles: string[] };
14
+ export interface Role {
15
+ id: string;
16
+ name: string;
17
+ isAdmin?: boolean;
18
+ }
19
+
20
+ type UserWithRoleIds<USER extends User = User> = Omit<USER, "roles"> & { roles: string[] };
16
21
 
17
- export interface UserManagementDelegateParams<CONTROLLER extends AuthController<any> = AuthController<any>> {
22
+ export interface UserManagementDelegateParams<CONTROLLER extends AuthController<User> = AuthController<User>> {
18
23
 
19
24
  authController: CONTROLLER;
20
25
 
@@ -49,11 +54,6 @@ export interface UserManagementDelegateParams<CONTROLLER extends AuthController<
49
54
  */
50
55
  allowDefaultRolesCreation?: boolean;
51
56
 
52
- /**
53
- * Include the collection config permissions in the user management system.
54
- */
55
- includeCollectionConfigPermissions?: boolean;
56
-
57
57
  }
58
58
 
59
59
  /**
@@ -65,18 +65,16 @@ export interface UserManagementDelegateParams<CONTROLLER extends AuthController<
65
65
  * @param rolesPath
66
66
  * @param roles
67
67
  * @param allowDefaultRolesCreation
68
- * @param includeCollectionConfigPermissions
69
68
  */
70
- export function useBuildUserManagement<CONTROLLER extends AuthController<any> = AuthController<any>,
71
- USER extends User = CONTROLLER extends AuthController<infer U> ? U : any>
69
+ export function useBuildUserManagement<CONTROLLER extends AuthController<User> = AuthController<User>,
70
+ USER extends User = CONTROLLER extends AuthController<infer U> ? U : User>
72
71
  ({
73
72
  authController,
74
73
  dataSourceDelegate,
75
74
  roles: rolesProp,
76
75
  usersPath = "__FIRECMS/config/users",
77
76
  rolesPath = "__FIRECMS/config/roles",
78
- allowDefaultRolesCreation,
79
- includeCollectionConfigPermissions
77
+ allowDefaultRolesCreation
80
78
  }: UserManagementDelegateParams<CONTROLLER>): UserManagementDelegate<USER> & CONTROLLER {
81
79
 
82
80
  if (!authController) {
@@ -125,10 +123,10 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
125
123
  }
126
124
  setRolesLoading(false);
127
125
  },
128
- onError(e: any): void {
126
+ onError(e: unknown): void {
129
127
  setRoles([]);
130
128
  console.error("Error loading roles", e);
131
- setRolesError(e);
129
+ setRolesError(e instanceof Error ? e : new Error(String(e)));
132
130
  setRolesLoading(false);
133
131
  }
134
132
  });
@@ -151,7 +149,7 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
151
149
  console.debug("Updating users", entities);
152
150
  setUsersError(undefined);
153
151
  try {
154
- const newUsers = entitiesToUsers(entities);
152
+ const newUsers = entitiesToUsers(entities) as UserWithRoleIds<USER>[];
155
153
  // if (!equal(newUsers, usersWithRoleIds))
156
154
  setUsersWithRoleIds(newUsers);
157
155
  } catch (e) {
@@ -161,10 +159,10 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
161
159
  }
162
160
  setUsersLoading(false);
163
161
  },
164
- onError(e: any): void {
162
+ onError(e: unknown): void {
165
163
  console.error("Error loading users", e);
166
164
  setUsersWithRoleIds([]);
167
- setUsersError(e);
165
+ setUsersError(e instanceof Error ? e : new Error(String(e)));
168
166
  setUsersLoading(false);
169
167
  }
170
168
  });
@@ -261,14 +259,14 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
261
259
 
262
260
 
263
261
 
264
- const defineRolesFor: ((user: User) => Role[] | undefined) = useCallback((user) => {
262
+ const defineRolesFor: ((user: User) => string[] | undefined) = useCallback((user) => {
265
263
  if (!usersWithRoleIds) throw Error("Users not loaded");
266
264
  const mgmtUser = usersWithRoleIds.find(u => u.email?.toLowerCase() === user?.email?.toLowerCase());
267
265
  if (!mgmtUser || !mgmtUser.roles) return undefined;
268
- return roles.filter(r => mgmtUser.roles.includes(r.id));
269
- }, [roles, usersWithRoleIds]);
266
+ return mgmtUser.roles;
267
+ }, [usersWithRoleIds]);
270
268
 
271
- const authenticator: Authenticator<USER> = useCallback(({ user }) => {
269
+ const accessGate: FirebaseAccessGate<USER> = useCallback(({ user }) => {
272
270
 
273
271
  if (loading) {
274
272
  return false;
@@ -310,9 +308,9 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
310
308
  }, [loading, users]);
311
309
 
312
310
  const userRoles = authController.user ? defineRolesFor(authController.user) : undefined;
313
- const isAdmin = (userRoles ?? []).some(r => r.id === "admin");
311
+ const isAdmin = (userRoles ?? []).some(r => r === "admin");
314
312
 
315
- const userRoleIds = userRoles?.map(r => r.id);
313
+ const userRoleIds = userRoles;
316
314
  useEffect(() => {
317
315
  console.debug("Setting user roles", {
318
316
  userRoles,
@@ -339,9 +337,8 @@ export function useBuildUserManagement<CONTROLLER extends AuthController<any> =
339
337
  usersError,
340
338
  isAdmin,
341
339
  allowDefaultRolesCreation: allowDefaultRolesCreation === undefined ? true : allowDefaultRolesCreation,
342
- includeCollectionConfigPermissions: Boolean(includeCollectionConfigPermissions),
343
340
  defineRolesFor,
344
- authenticator,
341
+ accessGate,
345
342
  ...authController,
346
343
  initialLoading: authController.initialLoading || loading,
347
344
  userRoles: userRoles,
@@ -358,8 +355,8 @@ const entitiesToUsers = (docs: Entity<Omit<UserWithRoleIds, "id">>[]): (UserWith
358
355
  const data = doc.values;
359
356
  const record = data as Record<string, unknown>;
360
357
  const newVar = {
361
- uid: doc.id,
362
358
  ...data,
359
+ uid: doc.id,
363
360
  created_on: record.created_on as Date | undefined,
364
361
  updated_on: record.updated_on as Date | undefined
365
362
  };
@@ -0,0 +1,145 @@
1
+
2
+ import { useCallback, useEffect, useRef, useState, useMemo } from "react";
3
+ import { deepEqual as equal } from "fast-equals";
4
+
5
+ import { AuthController, RebaseData, StorageSource, User } from "@rebasepro/types";
6
+
7
+ /**
8
+ * Client-side gate that decides whether a Firebase-authenticated user
9
+ * is allowed to access the CMS UI.
10
+ *
11
+ * Return `true` to allow access or `false` / throw to deny.
12
+ * @group Firebase
13
+ */
14
+ export type FirebaseAccessGate<USER extends User = User> = (props: {
15
+
16
+ /**
17
+ * Logged-in user or null
18
+ */
19
+ user: USER | null;
20
+
21
+ /**
22
+ * AuthController
23
+ */
24
+ authController: AuthController<USER>;
25
+
26
+ /**
27
+ * Unified data access API
28
+ */
29
+ data: RebaseData;
30
+
31
+ /**
32
+ * Used storage implementation
33
+ */
34
+ storageSource: StorageSource;
35
+
36
+ }) => boolean | Promise<boolean>;
37
+
38
+ /**
39
+ * Hook that evaluates a {@link FirebaseAccessGate} callback after
40
+ * the user logs in and gates access to the main CMS view.
41
+ *
42
+ * @group Firebase
43
+ */
44
+ export function useFirebaseAccessGate<USER extends User = User>
45
+ ({
46
+ disabled,
47
+ authController,
48
+ accessGate,
49
+ storageSource,
50
+ data
51
+ }:
52
+ {
53
+ disabled?: boolean,
54
+ authController: AuthController<USER>,
55
+ accessGate?: boolean | FirebaseAccessGate<USER>,
56
+ data: RebaseData;
57
+ storageSource: StorageSource;
58
+ }): {
59
+ canAccessMainView: boolean,
60
+ authLoading: boolean,
61
+ notAllowedError: unknown,
62
+ authVerified: boolean,
63
+ } {
64
+
65
+ const gateEnabled = Boolean(accessGate);
66
+
67
+ const [authLoading, setAuthLoading] = useState<boolean>(gateEnabled);
68
+ const [notAllowedError, setNotAllowedError] = useState<unknown>(false);
69
+ const [authVerified, setAuthVerified] = useState<boolean>(!gateEnabled || Boolean(authController.loginSkipped));
70
+
71
+ const canAccessMainView = (authVerified) &&
72
+ (!gateEnabled || Boolean(authController.user) || Boolean(authController.loginSkipped)) &&
73
+ !notAllowedError;
74
+
75
+ useEffect(() => {
76
+ if (authController.loginSkipped)
77
+ setAuthVerified(true);
78
+ }, [authController.loginSkipped]);
79
+
80
+ /**
81
+ * We use this ref to check the authentication only if the user has
82
+ * changed.
83
+ */
84
+ const checkedUserRef = useRef<User | undefined>(undefined);
85
+
86
+ const checkAccess = useCallback(async () => {
87
+
88
+ if (disabled) {
89
+ return;
90
+ }
91
+
92
+ if (authController.initialLoading) {
93
+ return;
94
+ }
95
+
96
+ if (!authController.user && !authController.loginSkipped) {
97
+ checkedUserRef.current = undefined;
98
+ setAuthLoading(false);
99
+ setAuthVerified(false);
100
+ return;
101
+ }
102
+
103
+ const delegateUser = authController.user;
104
+
105
+ if (accessGate instanceof Function && delegateUser && !equal(checkedUserRef.current?.uid, delegateUser.uid)) {
106
+ setAuthLoading(true);
107
+ try {
108
+ const allowed = await accessGate({
109
+ user: delegateUser,
110
+ authController,
111
+ data,
112
+ storageSource
113
+ });
114
+ if (!allowed) {
115
+ authController.signOut();
116
+ setNotAllowedError(true);
117
+ }
118
+ } catch (e) {
119
+ setNotAllowedError(e);
120
+ authController.signOut();
121
+ }
122
+ setAuthLoading(false);
123
+ setAuthVerified(true);
124
+ checkedUserRef.current = delegateUser;
125
+ } else {
126
+ setAuthLoading(false);
127
+ }
128
+
129
+ if (!authController.initialLoading && !delegateUser) {
130
+ setAuthVerified(true);
131
+ }
132
+
133
+ }, [disabled, authController, accessGate, data, storageSource]);
134
+
135
+ useEffect(() => {
136
+ checkAccess();
137
+ }, [checkAccess]);
138
+
139
+ return useMemo(() => ({
140
+ canAccessMainView,
141
+ authLoading: gateEnabled && authLoading,
142
+ notAllowedError,
143
+ authVerified
144
+ }), [canAccessMainView, gateEnabled, authLoading, notAllowedError, authVerified]);
145
+ }
@@ -24,14 +24,14 @@ import {
24
24
  } from "@firebase/auth";
25
25
  import { FirebaseApp } from "@firebase/app";
26
26
  import { FirebaseAuthController, FirebaseSignInOption, FirebaseSignInProvider, FirebaseUserWrapper } from "../types";
27
- import { Role, User } from "@rebasepro/types";
27
+ import type { User } from "@rebasepro/types";
28
28
 
29
29
  export interface FirebaseAuthControllerProps {
30
30
  loading?: boolean;
31
31
  firebaseApp?: FirebaseApp;
32
32
  signInOptions?: Array<FirebaseSignInProvider | FirebaseSignInOption>;
33
33
  onSignOut?: () => void;
34
- defineRolesFor?: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
34
+ defineRolesFor?: (user: User) => Promise<string[] | undefined> | string[] | undefined;
35
35
  }
36
36
 
37
37
  /**
@@ -53,13 +53,11 @@ export const useFirebaseAuthController = <USER extends FirebaseUserWrapper = any
53
53
  const [authLoading, setAuthLoading] = useState(true);
54
54
  const [loginSkipped, setLoginSkipped] = useState<boolean>(false);
55
55
  const [confirmationResult, setConfirmationResult] = useState<undefined | ConfirmationResult>();
56
- const [userRoles, _setUserRoles] = useState<Role[] | undefined>();
56
+ const [userRoles, _setUserRoles] = useState<string[] | undefined>();
57
57
  const [extra, setExtra] = useState<any>();
58
58
 
59
- const setUserRoles = useCallback((roles: Role[] | undefined) => {
60
- const currentRoleIds = userRoles?.map(r => r.id);
61
- const newRoleIds = roles?.map(r => r.id);
62
- if (!equal(currentRoleIds, newRoleIds)) {
59
+ const setUserRoles = useCallback((roles: string[] | undefined) => {
60
+ if (!equal(userRoles, roles)) {
63
61
  _setUserRoles(roles);
64
62
  }
65
63
  }, [userRoles]);
@@ -299,7 +297,7 @@ export const useFirebaseAuthController = <USER extends FirebaseUserWrapper = any
299
297
  const firebaseUserWrapper: FirebaseUserWrapper | null = loggedUser
300
298
  ? {
301
299
  ...loggedUser,
302
- roles: userRoles?.map(r => r.id), // User.roles is string[], keep Role[] internally only
300
+ roles: userRoles,
303
301
  firebaseUser: loggedUser
304
302
  }
305
303
  : null;
@@ -15,7 +15,7 @@ import {
15
15
  startAt
16
16
  } from "@firebase/database";
17
17
  import { useCallback } from "react";
18
- import { DataDriver, DeleteEntityProps, Entity, FetchCollectionProps, FetchEntityProps, ListenCollectionProps, ListenEntityProps, SaveEntityProps } from "@rebasepro/types";
18
+ import { DataDriver, DeleteEntityProps, Entity, EntityCollection, FetchCollectionProps, FetchEntityProps, FilterValues, ListenCollectionProps, ListenEntityProps, SaveEntityProps } from "@rebasepro/types";
19
19
 
20
20
  export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: FirebaseApp }): DataDriver {
21
21
 
@@ -167,14 +167,14 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
167
167
  }, [firebaseApp]);
168
168
 
169
169
  // Implementing additional methods required by DataDriver
170
- const checkUniqueField = useCallback(async (slug: string, name: string, value: any, entityId?: string | number): Promise<boolean> => {
170
+ const checkUniqueField = useCallback(async (slug: string, name: string, value: unknown, entityId?: string | number): Promise<boolean> => {
171
171
  if (!firebaseApp) {
172
172
  throw new Error("Firebase app not provided");
173
173
  }
174
174
  const database = getDatabase(firebaseApp);
175
175
 
176
176
  // Simplified example; the Realtime Database does not support querying with "not equal" conditions
177
- const dbRef = query(ref(database, slug), orderByChild(name), startAt(value), limitToFirst(1));
177
+ const dbRef = query(ref(database, slug), orderByChild(name), startAt(value as string | number | boolean | null), limitToFirst(1));
178
178
  const snapshot = await get(dbRef);
179
179
 
180
180
  if (!snapshot.exists()) {
@@ -194,7 +194,11 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
194
194
  path,
195
195
  filter,
196
196
  sortBy
197
- }: any): boolean => {
197
+ }: {
198
+ path: string;
199
+ filter?: FilterValues<string>;
200
+ sortBy?: [string, "asc" | "desc"];
201
+ }): boolean => {
198
202
  return false;
199
203
  }, []);
200
204
 
@@ -217,7 +221,7 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
217
221
  * Transform data from RTDB format back to CMS format
218
222
  * This is used internally when fetching/listening to data
219
223
  */
220
- function delegateToCMSModel(data: any): any {
224
+ function delegateToCMSModel(data: unknown): unknown {
221
225
  if (data === null || data === undefined) return null;
222
226
 
223
227
  if (Array.isArray(data)) {
@@ -225,9 +229,9 @@ function delegateToCMSModel(data: any): any {
225
229
  }
226
230
 
227
231
  if (typeof data === "object") {
228
- const result: Record<string, any> = {};
229
- for (const key of Object.keys(data)) {
230
- const childValue = delegateToCMSModel(data[key]);
232
+ const result: Record<string, unknown> = {};
233
+ for (const key of Object.keys(data as Record<string, unknown>)) {
234
+ const childValue = delegateToCMSModel((data as Record<string, unknown>)[key]);
231
235
  if (childValue !== undefined)
232
236
  result[key] = childValue;
233
237
  }
@@ -241,20 +245,21 @@ function delegateToCMSModel(data: any): any {
241
245
  * Transform data from CMS format to RTDB format
242
246
  * This is used internally when saving data
243
247
  */
244
- function cmsToRTDBModel(data: any, database: Database): any {
248
+ function cmsToRTDBModel(data: unknown, database: Database): unknown {
245
249
  if (data === undefined) {
246
250
  return null;
247
251
  } else if (data === null) {
248
252
  return null;
249
253
  } else if (Array.isArray(data)) {
250
254
  return data.filter(v => v !== undefined).map(v => cmsToRTDBModel(v, database));
251
- } else if (data.isEntityReference && data.isEntityReference()) {
252
- return ref(database, `${data.slug}/${data.id}`);
255
+ } else if (typeof data === "object" && data !== null && "isEntityReference" in data && typeof (data as Record<string, unknown>).isEntityReference === "function" && (data as { isEntityReference: () => boolean }).isEntityReference()) {
256
+ const entityRef = data as unknown as { slug: string; id: string };
257
+ return ref(database, `${entityRef.slug}/${entityRef.id}`);
253
258
  } else if (data instanceof Date) {
254
259
  // For dates, convert to ISO string or timestamp.
255
260
  return data.toISOString();
256
261
  } else if (data && typeof data === "object") {
257
- return Object.entries(data)
262
+ return Object.entries(data as Record<string, unknown>)
258
263
  .map(([key, v]) => {
259
264
  const rtdbModel = cmsToRTDBModel(v, database);
260
265
  if (rtdbModel !== undefined)
@@ -132,8 +132,8 @@ export function useFirebaseStorageSource({
132
132
  const response = await fetch(url);
133
133
  const blob = await response.blob();
134
134
  return new File([blob], path);
135
- } catch (e: any) {
136
- if (e?.code === "storage/object-not-found") return null;
135
+ } catch (e: unknown) {
136
+ if (typeof e === "object" && e !== null && "code" in e && (e as { code: string }).code === "storage/object-not-found") return null;
137
137
  throw e;
138
138
  }
139
139
  },
@@ -170,8 +170,8 @@ export function useFirebaseStorageSource({
170
170
  }
171
171
  urlsCache[storagePathOrUrl] = result;
172
172
  return result;
173
- } catch (e: any) {
174
- if (e?.code === "storage/object-not-found") return {
173
+ } catch (e: unknown) {
174
+ if (typeof e === "object" && e !== null && "code" in e && (e as { code: string }).code === "storage/object-not-found") return {
175
175
  url: null,
176
176
  fileNotFound: true
177
177
  };