@rebasepro/client-firebase 0.8.0 → 0.9.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.
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { AnalyticsEvent, AppView, AppViewsBuilder, EntityCollection, EntityCollectionsBuilder, RebasePlugin, Locale, PropertyConfig } from "@rebasepro/types";
2
+ import { AnalyticsEvent, AppView, AppViewsBuilder, CollectionConfig, CollectionConfigsBuilder, RebasePlugin, Locale, PropertyConfig } from "@rebasepro/types";
3
3
  import { FirebaseAccessGate } from "../hooks/useFirebaseAccessGate";
4
4
  import { FirebaseApp } from "@firebase/app";
5
5
  import { FirebaseLoginViewProps } from "./FirebaseLoginView";
@@ -29,7 +29,7 @@ export type RebaseFirebaseAppProps = {
29
29
  * Each of the navigation entries in this field
30
30
  * generates an entry in the main menu.
31
31
  */
32
- collections?: EntityCollection[] | EntityCollectionsBuilder;
32
+ collections?: CollectionConfig[] | CollectionConfigsBuilder;
33
33
  /**
34
34
  * Custom additional views created by the developer, added to the main
35
35
  * navigation
@@ -70,6 +70,7 @@ export declare function useBuildUserManagement<CONTROLLER extends AuthController
70
70
  photoURL: string | null;
71
71
  providerId: string;
72
72
  isAnonymous: boolean;
73
+ emailVerified?: boolean;
73
74
  createdAt?: Date | string | null;
74
75
  metadata?: Record<string, any>;
75
76
  getIdToken?: (forceRefresh?: boolean) => Promise<string>;
@@ -1,4 +1,4 @@
1
- import { DataDriver, EntityCollection, FilterCombination } from "@rebasepro/types";
1
+ import { DataDriver, CollectionConfig, FilterCombination } from "@rebasepro/types";
2
2
  import { Firestore } from "@firebase/firestore";
3
3
  import { FirebaseApp } from "@firebase/app";
4
4
  import { FirestoreTextSearchControllerBuilder } from "../types/text_search";
@@ -26,13 +26,13 @@ export interface FirestoreDataDriverProps {
26
26
  }
27
27
  export type FirestoreIndexesBuilder = (params: {
28
28
  path: string;
29
- collection: EntityCollection<any>;
29
+ collection: CollectionConfig<any>;
30
30
  }) => FilterCombination<string>[] | undefined;
31
31
  export type FirestoreDataDriver = DataDriver & {
32
32
  initTextSearch: (props: {
33
33
  path: string;
34
34
  databaseId?: string;
35
- collection?: EntityCollection;
35
+ collection?: CollectionConfig;
36
36
  }) => Promise<boolean>;
37
37
  };
38
38
  /**
package/dist/index.es.js CHANGED
@@ -4,7 +4,7 @@ import { FacebookAuthProvider, GithubAuthProvider, GoogleAuthProvider, OAuthProv
4
4
  import { deleteObject, getDownloadURL, getMetadata, getStorage, list, ref, uploadBytesResumable } from "@firebase/storage";
5
5
  import { deleteApp, getApps, initializeApp } from "@firebase/app";
6
6
  import { getToken, initializeAppCheck } from "@firebase/app-check";
7
- import { EntityReference, GeoPoint } from "@rebasepro/types";
7
+ import { DEFAULT_DATA_SOURCE_KEY, EntityReference, GeoPoint } from "@rebasepro/types";
8
8
  import { DocumentReference, GeoPoint as GeoPoint$1, Timestamp, VectorValue, collection, deleteDoc, deleteField, doc, getCountFromServer, getDoc, getDocs, getFirestore, limit, onSnapshot, orderBy, query, serverTimestamp, setDoc, startAfter, vector, where } from "@firebase/firestore";
9
9
  import { COLLECTION_PATH_SEPARATOR, buildRebaseData, sortProperties, stripCollectionPath } from "@rebasepro/common";
10
10
  import Fuse from "fuse.js";
@@ -12,7 +12,7 @@ import { getFunctions, httpsCallable } from "@firebase/functions";
12
12
  import { get, getDatabase, limitToFirst, onValue, orderByChild, orderByKey, push, query as query$1, ref as ref$1, remove, set, startAt } from "@firebase/database";
13
13
  import { removeUndefined } from "@rebasepro/utils";
14
14
  import { AdminModeControllerProvider, ErrorView, ModeControllerProvider, Rebase, RebaseLogo, RebaseRoutes, SnackbarProvider, useBrowserTitleAndIcon, useBuildAdminModeController, useBuildLocalConfigurationPersistence, useBuildModeController, useModeController, useSnackbarController } from "@rebasepro/core";
15
- import { AppBar, CollectionRegistryContext, Drawer, NavigationStateContext, RebaseRoute, Scaffold, SideDialogs, SideEntityProvider, UrlContext, useBuildCollectionRegistryController, useBuildNavigationStateController, useBuildUrlController } from "@rebasepro/admin";
15
+ import { AppBar, CollectionRegistryContext, Drawer, NavigationStateContext, RebaseRoute, Scaffold, SideDialogs, SidePanelProvider, UrlContext, useBuildCollectionRegistryController, useBuildNavigationStateController, useBuildUrlController } from "@rebasepro/admin";
16
16
  import { ArrowLeftIcon, Button, CenteredView, CircularProgress, CircularProgressCenter, IconButton, LoadingButton, MailIcon, PhoneIcon, TextField, Typography, UserIcon, cls, iconSize } from "@rebasepro/ui";
17
17
  import { Navigate, Outlet, Route } from "react-router-dom";
18
18
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -262,8 +262,8 @@ function useFirebaseStorageSource({ firebaseApp, bucketUrl }) {
262
262
  }, 5e3);
263
263
  };
264
264
  setProgressTimeout();
265
- uploadTask.on("state_changed", (snapshot) => {
266
- const progress = snapshot.bytesTransferred / snapshot.totalBytes * 100;
265
+ uploadTask.on("state_changed", (entity) => {
266
+ const progress = entity.bytesTransferred / entity.totalBytes * 100;
267
267
  if (progress > lastProgress) {
268
268
  lastProgress = progress;
269
269
  setProgressTimeout();
@@ -586,8 +586,8 @@ function normalizePropertiesEnumValues(properties, sortObjectFormat = false) {
586
586
  //#region src/utils/database.ts
587
587
  async function getFirestoreDataInPath(firebaseApp, path, parentPaths, limit$2) {
588
588
  const firestore = getFirestore(firebaseApp);
589
- if (!parentPaths || parentPaths.length === 0) return getDocs(query(collection(firestore, path), limit(limit$2))).then((querySnapshot) => {
590
- return querySnapshot.docs.map((doc) => doc.data());
589
+ if (!parentPaths || parentPaths.length === 0) return getDocs(query(collection(firestore, path), limit(limit$2))).then((queryEntity) => {
590
+ return queryEntity.docs.map((doc) => doc.data());
591
591
  });
592
592
  else {
593
593
  let currentDocs = void 0;
@@ -699,9 +699,9 @@ var localSearchControllerBuilder = ({ firebaseApp }) => {
699
699
  if (collectionProp) {
700
700
  console.debug("Init local search controller", path);
701
701
  listeners[path] = onSnapshot(query(collection(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), path)), {
702
- next: (snapshot) => {
703
- if (snapshot.metadata.fromCache && snapshot.metadata.hasPendingWrites) return;
704
- const docs = snapshot.docs.map((doc) => ({
702
+ next: (entity) => {
703
+ if (entity.metadata.fromCache && entity.metadata.hasPendingWrites) return;
704
+ const docs = entity.docs.map((doc) => ({
705
705
  id: doc.id,
706
706
  ...doc.data()
707
707
  }));
@@ -968,6 +968,15 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
968
968
  const queryParams = [];
969
969
  if (filter) Object.entries(filter).filter(([_, entry]) => !!entry).forEach(([key, filterParameter]) => {
970
970
  const [op, value] = filterParameter;
971
+ if (op === "is-null") {
972
+ queryParams.push(where(key, "==", null));
973
+ return;
974
+ }
975
+ if (op === "is-not-null") {
976
+ queryParams.push(where(key, "!=", null));
977
+ return;
978
+ }
979
+ if (op === "like" || op === "ilike" || op === "not-like" || op === "not-ilike") throw new Error(`Firestore does not support the "${op}" operator (SQL pattern matching). Use a full-text search index or the collection's searchString instead.`);
971
980
  queryParams.push(where(key, op, cmsToFirestoreModel(value, firestore)));
972
981
  });
973
982
  if (orderBy$1 && order) queryParams.push(orderBy(orderBy$1, order));
@@ -975,19 +984,19 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
975
984
  if (limit$1) queryParams.push(limit(limit$1));
976
985
  return query(collectionReference, ...queryParams);
977
986
  }, [firebaseApp]);
978
- const getAndBuildEntity = useCallback((path, entityId, databaseId) => {
987
+ const getAndBuildEntity = useCallback((path, id, databaseId) => {
979
988
  if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
980
- return getDoc(doc(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), path, String(entityId))).then((docSnapshot) => {
981
- if (!docSnapshot.exists()) return;
982
- return createEntityFromDocument(docSnapshot, databaseId);
989
+ return getDoc(doc(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), path, String(id))).then((docEntity) => {
990
+ if (!docEntity.exists()) return;
991
+ return createRowFromDocument(docEntity);
983
992
  });
984
993
  }, [firebaseApp]);
985
- const listenEntity = useCallback(({ path, entityId, collection, onUpdate, onError }) => {
994
+ const listenOne = useCallback(({ path, id, collection, onUpdate, onError }) => {
986
995
  if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
987
996
  const databaseId = collection?.databaseId;
988
- return onSnapshot(doc(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), path, String(entityId)), {
989
- next: (docSnapshot) => {
990
- onUpdate(createEntityFromDocument(docSnapshot, databaseId));
997
+ return onSnapshot(doc(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), path, String(id)), {
998
+ next: (docEntity) => {
999
+ onUpdate(docEntity.exists() ? createRowFromDocument(docEntity) : null);
991
1000
  },
992
1001
  error: onError
993
1002
  });
@@ -1010,22 +1019,23 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1010
1019
  subscriptions = [];
1011
1020
  onUpdate([]);
1012
1021
  }
1013
- const entities = [];
1022
+ const rows = [];
1014
1023
  const addedEntitiesSet = /* @__PURE__ */ new Set();
1015
- subscriptions = (ids ?? []).map((entityId) => {
1016
- return listenEntity({
1024
+ subscriptions = (ids ?? []).map((id) => {
1025
+ return listenOne({
1017
1026
  path,
1018
- entityId,
1019
- onUpdate: (entity) => {
1020
- if (entity?.values) {
1021
- if (entity.id && !addedEntitiesSet.has(entity.id)) {
1022
- addedEntitiesSet.add(entity.id);
1023
- entities.push(entity);
1024
- onUpdate(entities);
1027
+ id,
1028
+ onUpdate: (row) => {
1029
+ const incomingId = row?.id;
1030
+ if (row && incomingId !== void 0) {
1031
+ if (!addedEntitiesSet.has(incomingId)) {
1032
+ addedEntitiesSet.add(incomingId);
1033
+ rows.push(row);
1034
+ onUpdate(rows);
1025
1035
  }
1026
- } else if (entity?.id) {
1027
- addedEntitiesSet.delete(entity.id);
1028
- onUpdate([...entities.filter((e) => e.id !== entityId)]);
1036
+ } else {
1037
+ addedEntitiesSet.delete(id);
1038
+ onUpdate([...rows.filter((r) => r.id !== id)]);
1029
1039
  }
1030
1040
  }
1031
1041
  });
@@ -1034,7 +1044,7 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1034
1044
  return () => {
1035
1045
  subscriptions.forEach((p) => p());
1036
1046
  };
1037
- }, [firebaseApp, listenEntity]);
1047
+ }, [firebaseApp, listenOne]);
1038
1048
  const initTextSearch = useCallback(async (props) => {
1039
1049
  console.debug("Init text search controller", searchControllerRef.current, props.path);
1040
1050
  if (!searchControllerRef.current) {
@@ -1059,7 +1069,7 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1059
1069
  * @param orderBy
1060
1070
  * @param order
1061
1071
  * @return Function to cancel subscription
1062
- * @see useCollectionFetch if you need this functionality implemented as a hook
1072
+ * @see useCollection if you need this functionality implemented as a hook
1063
1073
  * @group Firestore
1064
1074
  */
1065
1075
  const fetchCollection = useCallback(async ({ path, filter, limit, startAfter, searchString, orderBy, order, collection }) => {
@@ -1073,7 +1083,7 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1073
1083
  orderBy,
1074
1084
  order
1075
1085
  });
1076
- return (await getDocs(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId))).docs.map((doc) => createEntityFromDocument(doc, databaseId));
1086
+ return (await getDocs(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId))).docs.map((doc) => createRowFromDocument(doc));
1077
1087
  }, [buildQuery]);
1078
1088
  /**
1079
1089
  * Listen to a entities in a given path
@@ -1088,7 +1098,7 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1088
1098
  * @param order
1089
1099
  * @param onUpdate
1090
1100
  * @return Function to cancel subscription
1091
- * @see useCollectionFetch if you need this functionality implemented as a hook
1101
+ * @see useCollection if you need this functionality implemented as a hook
1092
1102
  * @group Firestore
1093
1103
  */
1094
1104
  const listenCollection = useCallback(({ path, filter, limit, startAfter, searchString, orderBy, order, onUpdate, onError, collection }) => {
@@ -1116,8 +1126,8 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1116
1126
  resolvedPath
1117
1127
  });
1118
1128
  return onSnapshot(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId), {
1119
- next: (snapshot) => {
1120
- if (!searchString) onUpdate(snapshot.docs.map((doc) => createEntityFromDocument(doc, databaseId)));
1129
+ next: (entity) => {
1130
+ if (!searchString) onUpdate(entity.docs.map((doc) => createRowFromDocument(doc)));
1121
1131
  },
1122
1132
  error: onError
1123
1133
  });
@@ -1127,38 +1137,38 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1127
1137
  performTextSearch
1128
1138
  ]);
1129
1139
  /**
1130
- * Retrieve an entity given a path and a collection
1140
+ * Retrieve a entity given a path and a collection
1131
1141
  * @param path
1132
- * @param entityId
1142
+ * @param id
1133
1143
  * @param collection
1134
1144
  * @group Firestore
1135
1145
  */
1136
- const fetchEntity = useCallback(({ path, entityId, collection }) => {
1137
- return getAndBuildEntity(path, entityId, collection?.databaseId);
1146
+ const fetchOne = useCallback(({ path, id, collection }) => {
1147
+ return getAndBuildEntity(path, id, collection?.databaseId);
1138
1148
  }, [getAndBuildEntity]);
1139
1149
  /**
1140
1150
  * Save entity to the specified path. Note that Firestore does not allow
1141
1151
  * undefined values.
1142
1152
  * @param path
1143
- * @param entityId
1153
+ * @param id
1144
1154
  * @param values
1145
1155
  * @param schemaId
1146
1156
  * @param collection
1147
1157
  * @param status
1148
1158
  * @group Firestore
1149
1159
  */
1150
- const saveEntity = useCallback(({ path, entityId, values: valuesProp, collection: collection$1, status }) => {
1160
+ const save = useCallback(({ path, id, values: valuesProp, collection: collection$1, status }) => {
1151
1161
  if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1152
1162
  console.debug("1", {
1153
1163
  path,
1154
- entityId,
1164
+ id,
1155
1165
  values: valuesProp,
1156
1166
  collection: collection$1
1157
1167
  });
1158
1168
  const values = cmsToFirestoreModel(valuesProp, getFirestore(firebaseApp));
1159
1169
  console.debug("2", {
1160
1170
  path,
1161
- entityId,
1171
+ id,
1162
1172
  values: valuesProp,
1163
1173
  collection: collection$1
1164
1174
  });
@@ -1166,18 +1176,17 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1166
1176
  const collectionReference = collection(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), path);
1167
1177
  console.debug("Saving entity", {
1168
1178
  path,
1169
- entityId,
1179
+ id,
1170
1180
  values,
1171
1181
  databaseId
1172
1182
  });
1173
1183
  let documentReference;
1174
- if (entityId) documentReference = doc(collectionReference, String(entityId));
1184
+ if (id) documentReference = doc(collectionReference, String(id));
1175
1185
  else documentReference = doc(collectionReference);
1176
1186
  return setDoc(documentReference, values, { merge: true }).then(() => {
1177
1187
  return {
1178
- id: documentReference.id,
1179
- path,
1180
- values: firestoreToCMSModel(values)
1188
+ ...firestoreToCMSModel(values),
1189
+ id: documentReference.id
1181
1190
  };
1182
1191
  }).catch((error) => {
1183
1192
  console.error("Error saving entity", error);
@@ -1185,15 +1194,15 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1185
1194
  });
1186
1195
  }, [firebaseApp]);
1187
1196
  /**
1188
- * Delete an entity
1197
+ * Delete a entity
1189
1198
  * @param entity
1190
1199
  * @param collection
1191
1200
  * @group Firestore
1192
1201
  */
1193
- const deleteEntity = useCallback(({ entity }) => {
1202
+ const deleteOne = useCallback(({ row, collection }) => {
1194
1203
  if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1195
- const databaseId = entity.databaseId;
1196
- return deleteDoc(doc(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), entity.path, String(entity.id)));
1204
+ const databaseId = collection?.databaseId;
1205
+ return deleteDoc(doc(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), row.path, String(row.id)));
1197
1206
  }, [firebaseApp]);
1198
1207
  /**
1199
1208
  * Check if the given property is unique in the given collection
@@ -1201,18 +1210,18 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1201
1210
  * @param name of the property
1202
1211
  * @param value
1203
1212
  * @param property
1204
- * @param entityId
1213
+ * @param id
1205
1214
  * @return `true` if there are no other fields besides the given entity
1206
1215
  * @group Firestore
1207
1216
  */
1208
- const checkUniqueField = useCallback(async (path, name, value, entityId, collection$2) => {
1217
+ const checkUniqueField = useCallback(async (path, name, value, id, collection$2) => {
1209
1218
  if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1210
1219
  const databaseId = collection$2?.databaseId;
1211
1220
  const firestore = databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp);
1212
1221
  if (value === void 0 || value === null) return Promise.resolve(true);
1213
- return (await getDocs(query(collection(firestore, path), where(name, "==", cmsToFirestoreModel(value, firestore))))).docs.filter((doc) => doc.id !== entityId).length === 0;
1222
+ return (await getDocs(query(collection(firestore, path), where(name, "==", cmsToFirestoreModel(value, firestore))))).docs.filter((doc) => doc.id !== id).length === 0;
1214
1223
  }, [firebaseApp]);
1215
- const countEntities = useCallback(async ({ path, filter, order, orderBy, collection }) => {
1224
+ const count = useCallback(async ({ path, filter, order, orderBy, collection }) => {
1216
1225
  if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1217
1226
  const databaseId = collection?.databaseId;
1218
1227
  return (await getCountFromServer(buildQuery(path, filter, orderBy, order, void 0, void 0, databaseId))).data().count;
@@ -1241,35 +1250,31 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1241
1250
  initTextSearch,
1242
1251
  fetchCollection,
1243
1252
  listenCollection,
1244
- fetchEntity,
1245
- listenEntity,
1246
- saveEntity,
1247
- deleteEntity,
1253
+ fetchOne,
1254
+ listenOne,
1255
+ save,
1256
+ delete: deleteOne,
1248
1257
  checkUniqueField,
1249
- countEntities,
1258
+ count,
1250
1259
  isFilterCombinationValid
1251
1260
  }), [
1252
1261
  firebaseApp,
1253
1262
  initTextSearch,
1254
1263
  fetchCollection,
1255
1264
  listenCollection,
1256
- fetchEntity,
1257
- listenEntity,
1258
- saveEntity,
1259
- deleteEntity,
1265
+ fetchOne,
1266
+ listenOne,
1267
+ save,
1268
+ deleteOne,
1260
1269
  checkUniqueField,
1261
- countEntities,
1270
+ count,
1262
1271
  isFilterCombinationValid
1263
1272
  ]);
1264
1273
  }
1265
- var createEntityFromDocument = (docSnap, databaseId) => {
1266
- const values = firestoreToCMSModel(docSnap.data());
1267
- const path = getCMSPathFromFirestorePath(docSnap.ref.path);
1274
+ var createRowFromDocument = (docSnap) => {
1268
1275
  return {
1269
- id: docSnap.id,
1270
- path,
1271
- values,
1272
- databaseId
1276
+ ...firestoreToCMSModel(docSnap.data()),
1277
+ id: docSnap.id
1273
1278
  };
1274
1279
  };
1275
1280
  /**
@@ -1384,70 +1389,65 @@ function useFirebaseRTDBDelegate({ firebaseApp }) {
1384
1389
  let dbQuery = query$1(ref$1(getDatabase(firebaseApp), path));
1385
1390
  if (startAfter !== void 0) dbQuery = query$1(dbQuery, orderByKey(), startAt(String(startAfter)));
1386
1391
  if (limit !== void 0) dbQuery = query$1(dbQuery, limitToFirst(limit));
1387
- const snapshot = await get(dbQuery);
1388
- if (snapshot.exists()) return Object.entries(snapshot.val()).map(([id, values]) => ({
1389
- id,
1390
- path,
1391
- values: delegateToCMSModel(values)
1392
+ const entity = await get(dbQuery);
1393
+ if (entity.exists()) return Object.entries(entity.val()).map(([id, values]) => ({
1394
+ ...delegateToCMSModel(values),
1395
+ id
1392
1396
  }));
1393
1397
  return [];
1394
1398
  }, [firebaseApp]),
1395
1399
  listenCollection: useCallback(({ path, onUpdate }) => {
1396
1400
  if (!firebaseApp) throw new Error("Firebase app not provided");
1397
- const unsubscribe = onValue(ref$1(getDatabase(firebaseApp), path), (snapshot) => {
1398
- if (snapshot.exists()) onUpdate(Object.entries(snapshot.val()).map(([id, values]) => ({
1399
- id,
1400
- path,
1401
- values: delegateToCMSModel(values)
1401
+ const unsubscribe = onValue(ref$1(getDatabase(firebaseApp), path), (entity) => {
1402
+ if (entity.exists()) onUpdate(Object.entries(entity.val()).map(([id, values]) => ({
1403
+ ...delegateToCMSModel(values),
1404
+ id
1402
1405
  })));
1403
1406
  else onUpdate([]);
1404
1407
  });
1405
1408
  return () => unsubscribe();
1406
1409
  }, [firebaseApp]),
1407
- fetchEntity: useCallback(async ({ path, entityId }) => {
1410
+ fetchOne: useCallback(async ({ path, id }) => {
1408
1411
  if (!firebaseApp) throw new Error("Firebase app not provided");
1409
- const snapshot = await get(ref$1(getDatabase(firebaseApp), `${path}/${entityId}`));
1410
- if (snapshot.exists()) return {
1411
- id: entityId,
1412
- path,
1413
- values: delegateToCMSModel(snapshot.val())
1412
+ const entity = await get(ref$1(getDatabase(firebaseApp), `${path}/${id}`));
1413
+ if (entity.exists()) return {
1414
+ ...delegateToCMSModel(entity.val()),
1415
+ id
1414
1416
  };
1415
1417
  }, [firebaseApp]),
1416
- listenEntity: useCallback(({ path, entityId, onUpdate, onError }) => {
1418
+ listenOne: useCallback(({ path, id, onUpdate, onError }) => {
1417
1419
  if (!firebaseApp) throw new Error("Firebase app not provided");
1418
- const unsubscribe = onValue(ref$1(getDatabase(firebaseApp), `${path}/${entityId}`), (snapshot) => {
1419
- if (snapshot.exists()) onUpdate({
1420
- id: entityId,
1421
- path,
1422
- values: delegateToCMSModel(snapshot.val())
1420
+ const unsubscribe = onValue(ref$1(getDatabase(firebaseApp), `${path}/${id}`), (entity) => {
1421
+ if (entity.exists()) onUpdate({
1422
+ ...delegateToCMSModel(entity.val()),
1423
+ id
1423
1424
  });
1424
1425
  else onError?.(/* @__PURE__ */ new Error("Entity does not exist"));
1425
1426
  });
1426
1427
  return () => unsubscribe();
1427
1428
  }, [firebaseApp]),
1428
- saveEntity: useCallback(async ({ path, entityId, values }) => {
1429
+ save: useCallback(async ({ path, id, values }) => {
1429
1430
  if (!firebaseApp) throw new Error("Firebase app not provided");
1430
1431
  const database = getDatabase(firebaseApp);
1431
- const finalId = entityId ?? push(ref$1(database, path)).key;
1432
+ const finalId = id ?? push(ref$1(database, path)).key;
1432
1433
  if (!finalId) throw new Error("Could not generate a new id");
1433
1434
  const transformedValues = cmsToRTDBModel(values, database);
1434
1435
  await set(ref$1(database, `${path}/${finalId}`), transformedValues);
1435
1436
  return {
1436
- id: finalId,
1437
- path,
1438
- values
1437
+ ...values,
1438
+ id: finalId
1439
1439
  };
1440
1440
  }, [firebaseApp]),
1441
- deleteEntity: useCallback(async ({ entity }) => {
1441
+ delete: useCallback(async ({ row }) => {
1442
1442
  if (!firebaseApp) throw new Error("Firebase app not provided");
1443
- await remove(ref$1(getDatabase(firebaseApp), `${entity.path}/${entity.id}`));
1443
+ await remove(ref$1(getDatabase(firebaseApp), `${row.path}/${row.id}`));
1444
1444
  }, [firebaseApp]),
1445
- checkUniqueField: useCallback(async (slug, name, value, entityId) => {
1445
+ checkUniqueField: useCallback(async (slug, name, value, id) => {
1446
1446
  if (!firebaseApp) throw new Error("Firebase app not provided");
1447
- const snapshot = await get(query$1(ref$1(getDatabase(firebaseApp), slug), orderByChild(name), startAt(value), limitToFirst(1)));
1448
- if (!snapshot.exists()) return true;
1449
- const [key, entityValue] = Object.entries(snapshot.val())[0];
1450
- if (entityValue && typeof entityValue === "object" && entityValue[name] === value && key === entityId) return true;
1447
+ const entity = await get(query$1(ref$1(getDatabase(firebaseApp), slug), orderByChild(name), startAt(value), limitToFirst(1)));
1448
+ if (!entity.exists()) return true;
1449
+ const [key, entityValue] = Object.entries(entity.val())[0];
1450
+ if (entityValue && typeof entityValue === "object" && entityValue[name] === value && key === id) return true;
1451
1451
  return false;
1452
1452
  }, [firebaseApp]),
1453
1453
  isFilterCombinationValid: useCallback(({ path, filter, sortBy }) => {
@@ -1537,11 +1537,11 @@ function useBuildUserManagement({ authController, dataSourceDelegate, roles: rol
1537
1537
  setRolesLoading(true);
1538
1538
  return dataSourceDelegate.listenCollection?.({
1539
1539
  path: rolesPath,
1540
- onUpdate(entities) {
1540
+ onUpdate(rows) {
1541
1541
  setRolesError(void 0);
1542
- console.debug("Updating roles", entities);
1542
+ console.debug("Updating roles", rows);
1543
1543
  try {
1544
- const newRoles = entityToRoles(entities);
1544
+ const newRoles = rowsToRoles(rows);
1545
1545
  if (!deepEqual(newRoles, roles)) setRoles(newRoles);
1546
1546
  } catch (e) {
1547
1547
  setRoles([]);
@@ -1571,11 +1571,11 @@ function useBuildUserManagement({ authController, dataSourceDelegate, roles: rol
1571
1571
  setUsersLoading(true);
1572
1572
  return dataSourceDelegate.listenCollection?.({
1573
1573
  path: usersPath,
1574
- onUpdate(entities) {
1575
- console.debug("Updating users", entities);
1574
+ onUpdate(rows) {
1575
+ console.debug("Updating users", rows);
1576
1576
  setUsersError(void 0);
1577
1577
  try {
1578
- setUsersWithRoleIds(entitiesToUsers(entities));
1578
+ setUsersWithRoleIds(rowsToUsers(rows));
1579
1579
  } catch (e) {
1580
1580
  setUsersWithRoleIds([]);
1581
1581
  console.error("Error loading users", e);
@@ -1610,21 +1610,21 @@ function useBuildUserManagement({ authController, dataSourceDelegate, roles: rol
1610
1610
  ...userExists ? {} : { created_on: /* @__PURE__ */ new Date() }
1611
1611
  };
1612
1612
  if (userExists && userExists.uid !== user.uid) {
1613
- const entity = {
1613
+ const row = {
1614
1614
  values: {},
1615
1615
  path: usersPath,
1616
1616
  id: userExists.uid
1617
1617
  };
1618
- await dataSourceDelegate.deleteEntity({ entity }).then(() => {
1618
+ await dataSourceDelegate.delete({ row }).then(() => {
1619
1619
  console.debug("Deleted previous user", userExists);
1620
1620
  }).catch((e) => {
1621
1621
  console.error("Error deleting user", e);
1622
1622
  });
1623
1623
  }
1624
- return dataSourceDelegate.saveEntity({
1624
+ return dataSourceDelegate.save({
1625
1625
  status: "existing",
1626
1626
  path: usersPath,
1627
- entityId: email,
1627
+ id: email,
1628
1628
  values: removeUndefined(data)
1629
1629
  }).then(() => user);
1630
1630
  }, [usersPath, dataSourceDelegate?.initialised]);
@@ -1633,10 +1633,10 @@ function useBuildUserManagement({ authController, dataSourceDelegate, roles: rol
1633
1633
  if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
1634
1634
  console.debug("Persisting role", role);
1635
1635
  const { id, ...roleData } = role;
1636
- return dataSourceDelegate.saveEntity({
1636
+ return dataSourceDelegate.save({
1637
1637
  status: "existing",
1638
1638
  path: rolesPath,
1639
- entityId: id,
1639
+ id,
1640
1640
  values: removeUndefined(roleData)
1641
1641
  }).then(() => {});
1642
1642
  }, [rolesPath, dataSourceDelegate?.initialised]);
@@ -1645,24 +1645,24 @@ function useBuildUserManagement({ authController, dataSourceDelegate, roles: rol
1645
1645
  if (!usersPath) throw Error("useBuildUserManagement Firestore not initialised");
1646
1646
  console.debug("Deleting", user);
1647
1647
  const { uid } = user;
1648
- const entity = {
1648
+ const row = {
1649
1649
  path: usersPath,
1650
1650
  id: uid,
1651
1651
  values: {}
1652
1652
  };
1653
- await dataSourceDelegate.deleteEntity({ entity });
1653
+ await dataSourceDelegate.delete({ row });
1654
1654
  }, [usersPath, dataSourceDelegate?.initialised]);
1655
1655
  const deleteRole = useCallback(async (role) => {
1656
1656
  if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
1657
1657
  if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
1658
1658
  console.debug("Deleting", role);
1659
1659
  const { id } = role;
1660
- const entity = {
1660
+ const row = {
1661
1661
  path: rolesPath,
1662
1662
  id,
1663
1663
  values: {}
1664
1664
  };
1665
- await dataSourceDelegate.deleteEntity({ entity });
1665
+ await dataSourceDelegate.delete({ row });
1666
1666
  }, [rolesPath, dataSourceDelegate?.initialised]);
1667
1667
  const defineRolesFor = useCallback((user) => {
1668
1668
  if (!usersWithRoleIds) throw Error("Users not loaded");
@@ -1738,22 +1738,21 @@ function useBuildUserManagement({ authController, dataSourceDelegate, roles: rol
1738
1738
  } : null
1739
1739
  };
1740
1740
  }
1741
- var entitiesToUsers = (docs) => {
1742
- return docs.map((doc) => {
1743
- const data = doc.values;
1744
- const record = data;
1741
+ var rowsToUsers = (rows) => {
1742
+ return rows.map((row) => {
1743
+ const { id, ...data } = row;
1745
1744
  return {
1746
1745
  ...data,
1747
- uid: doc.id,
1748
- created_on: record.created_on,
1749
- updated_on: record.updated_on
1746
+ uid: id,
1747
+ created_on: data.created_on,
1748
+ updated_on: data.updated_on
1750
1749
  };
1751
1750
  });
1752
1751
  };
1753
- var entityToRoles = (entities) => {
1754
- return entities.map((doc) => ({
1755
- id: doc.id,
1756
- ...doc.values
1752
+ var rowsToRoles = (rows) => {
1753
+ return rows.map((row) => ({
1754
+ ...row,
1755
+ id: row.id
1757
1756
  }));
1758
1757
  };
1759
1758
  //#endregion
@@ -2581,7 +2580,12 @@ function RebaseFirebaseApp({ name, logo, logoDark, accessGate, collections, view
2581
2580
  authController,
2582
2581
  userConfigPersistence,
2583
2582
  dateTimeFormat,
2584
- driver: firestoreDelegate,
2583
+ dataSources: [{
2584
+ key: DEFAULT_DATA_SOURCE_KEY,
2585
+ engine: "firestore",
2586
+ transport: "direct",
2587
+ driver: firestoreDelegate
2588
+ }],
2585
2589
  storageSource,
2586
2590
  entityLinkBuilder: ({ entity }) => `https://console.firebase.google.com/project/${firebaseApp.options.projectId}/firestore/data/${entity.path}/${entity.id}`,
2587
2591
  locale,
@@ -2607,7 +2611,7 @@ function RebaseFirebaseApp({ name, logo, logoDark, accessGate, collections, view
2607
2611
  to: urlController.buildUrlCollectionPath(firstCollectionEntry.id),
2608
2612
  replace: true
2609
2613
  }) : /* @__PURE__ */ jsx(CenteredView, { children: "No home page or collections provided." });
2610
- component = /* @__PURE__ */ jsx(SideEntityProvider, { children: /* @__PURE__ */ jsx(RebaseRoutes, { children: /* @__PURE__ */ jsxs(Route, {
2614
+ component = /* @__PURE__ */ jsx(SidePanelProvider, { children: /* @__PURE__ */ jsx(RebaseRoutes, { children: /* @__PURE__ */ jsxs(Route, {
2611
2615
  element: /* @__PURE__ */ jsxs(Scaffold, {
2612
2616
  logo: usedLogo,
2613
2617
  autoOpenDrawer,