@rebasepro/client-firebase 0.7.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.
package/dist/index.umd.js CHANGED
@@ -292,8 +292,8 @@
292
292
  }, 5e3);
293
293
  };
294
294
  setProgressTimeout();
295
- uploadTask.on("state_changed", (snapshot) => {
296
- const progress = snapshot.bytesTransferred / snapshot.totalBytes * 100;
295
+ uploadTask.on("state_changed", (entity) => {
296
+ const progress = entity.bytesTransferred / entity.totalBytes * 100;
297
297
  if (progress > lastProgress) {
298
298
  lastProgress = progress;
299
299
  setProgressTimeout();
@@ -616,8 +616,8 @@
616
616
  //#region src/utils/database.ts
617
617
  async function getFirestoreDataInPath(firebaseApp, path, parentPaths, limit) {
618
618
  const firestore = (0, _firebase_firestore.getFirestore)(firebaseApp);
619
- if (!parentPaths || parentPaths.length === 0) return (0, _firebase_firestore.getDocs)((0, _firebase_firestore.query)((0, _firebase_firestore.collection)(firestore, path), (0, _firebase_firestore.limit)(limit))).then((querySnapshot) => {
620
- return querySnapshot.docs.map((doc) => doc.data());
619
+ if (!parentPaths || parentPaths.length === 0) return (0, _firebase_firestore.getDocs)((0, _firebase_firestore.query)((0, _firebase_firestore.collection)(firestore, path), (0, _firebase_firestore.limit)(limit))).then((queryEntity) => {
620
+ return queryEntity.docs.map((doc) => doc.data());
621
621
  });
622
622
  else {
623
623
  let currentDocs = void 0;
@@ -729,9 +729,9 @@
729
729
  if (collectionProp) {
730
730
  console.debug("Init local search controller", path);
731
731
  listeners[path] = (0, _firebase_firestore.onSnapshot)((0, _firebase_firestore.query)((0, _firebase_firestore.collection)(databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp), path)), {
732
- next: (snapshot) => {
733
- if (snapshot.metadata.fromCache && snapshot.metadata.hasPendingWrites) return;
734
- const docs = snapshot.docs.map((doc) => ({
732
+ next: (entity) => {
733
+ if (entity.metadata.fromCache && entity.metadata.hasPendingWrites) return;
734
+ const docs = entity.docs.map((doc) => ({
735
735
  id: doc.id,
736
736
  ...doc.data()
737
737
  }));
@@ -998,6 +998,15 @@
998
998
  const queryParams = [];
999
999
  if (filter) Object.entries(filter).filter(([_, entry]) => !!entry).forEach(([key, filterParameter]) => {
1000
1000
  const [op, value] = filterParameter;
1001
+ if (op === "is-null") {
1002
+ queryParams.push((0, _firebase_firestore.where)(key, "==", null));
1003
+ return;
1004
+ }
1005
+ if (op === "is-not-null") {
1006
+ queryParams.push((0, _firebase_firestore.where)(key, "!=", null));
1007
+ return;
1008
+ }
1009
+ 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.`);
1001
1010
  queryParams.push((0, _firebase_firestore.where)(key, op, cmsToFirestoreModel(value, firestore)));
1002
1011
  });
1003
1012
  if (orderBy && order) queryParams.push((0, _firebase_firestore.orderBy)(orderBy, order));
@@ -1005,19 +1014,19 @@
1005
1014
  if (limit) queryParams.push((0, _firebase_firestore.limit)(limit));
1006
1015
  return (0, _firebase_firestore.query)(collectionReference, ...queryParams);
1007
1016
  }, [firebaseApp]);
1008
- const getAndBuildEntity = (0, react.useCallback)((path, entityId, databaseId) => {
1017
+ const getAndBuildEntity = (0, react.useCallback)((path, id, databaseId) => {
1009
1018
  if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1010
- return (0, _firebase_firestore.getDoc)((0, _firebase_firestore.doc)(databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp), path, String(entityId))).then((docSnapshot) => {
1011
- if (!docSnapshot.exists()) return;
1012
- return createEntityFromDocument(docSnapshot, databaseId);
1019
+ return (0, _firebase_firestore.getDoc)((0, _firebase_firestore.doc)(databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp), path, String(id))).then((docEntity) => {
1020
+ if (!docEntity.exists()) return;
1021
+ return createRowFromDocument(docEntity);
1013
1022
  });
1014
1023
  }, [firebaseApp]);
1015
- const listenEntity = (0, react.useCallback)(({ path, entityId, collection, onUpdate, onError }) => {
1024
+ const listenOne = (0, react.useCallback)(({ path, id, collection, onUpdate, onError }) => {
1016
1025
  if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1017
1026
  const databaseId = collection?.databaseId;
1018
- return (0, _firebase_firestore.onSnapshot)((0, _firebase_firestore.doc)(databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp), path, String(entityId)), {
1019
- next: (docSnapshot) => {
1020
- onUpdate(createEntityFromDocument(docSnapshot, databaseId));
1027
+ return (0, _firebase_firestore.onSnapshot)((0, _firebase_firestore.doc)(databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp), path, String(id)), {
1028
+ next: (docEntity) => {
1029
+ onUpdate(docEntity.exists() ? createRowFromDocument(docEntity) : null);
1021
1030
  },
1022
1031
  error: onError
1023
1032
  });
@@ -1040,22 +1049,23 @@
1040
1049
  subscriptions = [];
1041
1050
  onUpdate([]);
1042
1051
  }
1043
- const entities = [];
1052
+ const rows = [];
1044
1053
  const addedEntitiesSet = /* @__PURE__ */ new Set();
1045
- subscriptions = (ids ?? []).map((entityId) => {
1046
- return listenEntity({
1054
+ subscriptions = (ids ?? []).map((id) => {
1055
+ return listenOne({
1047
1056
  path,
1048
- entityId,
1049
- onUpdate: (entity) => {
1050
- if (entity?.values) {
1051
- if (entity.id && !addedEntitiesSet.has(entity.id)) {
1052
- addedEntitiesSet.add(entity.id);
1053
- entities.push(entity);
1054
- onUpdate(entities);
1057
+ id,
1058
+ onUpdate: (row) => {
1059
+ const incomingId = row?.id;
1060
+ if (row && incomingId !== void 0) {
1061
+ if (!addedEntitiesSet.has(incomingId)) {
1062
+ addedEntitiesSet.add(incomingId);
1063
+ rows.push(row);
1064
+ onUpdate(rows);
1055
1065
  }
1056
- } else if (entity?.id) {
1057
- addedEntitiesSet.delete(entity.id);
1058
- onUpdate([...entities.filter((e) => e.id !== entityId)]);
1066
+ } else {
1067
+ addedEntitiesSet.delete(id);
1068
+ onUpdate([...rows.filter((r) => r.id !== id)]);
1059
1069
  }
1060
1070
  }
1061
1071
  });
@@ -1064,231 +1074,237 @@
1064
1074
  return () => {
1065
1075
  subscriptions.forEach((p) => p());
1066
1076
  };
1067
- }, [firebaseApp, listenEntity]);
1068
- return {
1077
+ }, [firebaseApp, listenOne]);
1078
+ const initTextSearch = (0, react.useCallback)(async (props) => {
1079
+ console.debug("Init text search controller", searchControllerRef.current, props.path);
1080
+ if (!searchControllerRef.current) {
1081
+ console.warn("You are trying to use text search, but have not provided a text search controller in `useFirestoreDriver`. You can also set the flag `localTextSearchEnabled` to use local search in `useFirestoreDriver`. Local text search can incur in performance issues and higher costs for large datasets.");
1082
+ return false;
1083
+ }
1084
+ try {
1085
+ return searchControllerRef.current.init(props);
1086
+ } catch (e) {
1087
+ console.error("Error initializing text search controller", e);
1088
+ return false;
1089
+ }
1090
+ }, []);
1091
+ /**
1092
+ * Fetch entities in a Firestore path
1093
+ * @param path
1094
+ * @param collection
1095
+ * @param filter
1096
+ * @param limit
1097
+ * @param startAfter
1098
+ * @param searchString
1099
+ * @param orderBy
1100
+ * @param order
1101
+ * @return Function to cancel subscription
1102
+ * @see useCollection if you need this functionality implemented as a hook
1103
+ * @group Firestore
1104
+ */
1105
+ const fetchCollection = (0, react.useCallback)(async ({ path, filter, limit, startAfter, searchString, orderBy, order, collection }) => {
1106
+ const databaseId = collection?.databaseId;
1107
+ const resolvedPath = path;
1108
+ console.debug("Fetching collection", {
1109
+ path,
1110
+ limit,
1111
+ filter,
1112
+ startAfter,
1113
+ orderBy,
1114
+ order
1115
+ });
1116
+ return (await (0, _firebase_firestore.getDocs)(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId))).docs.map((doc) => createRowFromDocument(doc));
1117
+ }, [buildQuery]);
1118
+ /**
1119
+ * Listen to a entities in a given path
1120
+ * @param path
1121
+ * @param collection
1122
+ * @param onError
1123
+ * @param filter
1124
+ * @param limit
1125
+ * @param startAfter
1126
+ * @param searchString
1127
+ * @param orderBy
1128
+ * @param order
1129
+ * @param onUpdate
1130
+ * @return Function to cancel subscription
1131
+ * @see useCollection if you need this functionality implemented as a hook
1132
+ * @group Firestore
1133
+ */
1134
+ const listenCollection = (0, react.useCallback)(({ path, filter, limit, startAfter, searchString, orderBy, order, onUpdate, onError, collection }) => {
1135
+ console.debug("Listening collection", {
1136
+ path,
1137
+ searchString,
1138
+ limit,
1139
+ filter,
1140
+ startAfter,
1141
+ orderBy,
1142
+ order,
1143
+ collection
1144
+ });
1145
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1146
+ const databaseId = collection?.databaseId;
1147
+ if (searchString) return performTextSearch({
1148
+ path,
1149
+ searchString,
1150
+ onUpdate,
1151
+ databaseId
1152
+ });
1153
+ const resolvedPath = path;
1154
+ console.debug("Resolved path for listening", {
1155
+ path,
1156
+ resolvedPath
1157
+ });
1158
+ return (0, _firebase_firestore.onSnapshot)(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId), {
1159
+ next: (entity) => {
1160
+ if (!searchString) onUpdate(entity.docs.map((doc) => createRowFromDocument(doc)));
1161
+ },
1162
+ error: onError
1163
+ });
1164
+ }, [
1165
+ buildQuery,
1166
+ firebaseApp,
1167
+ performTextSearch
1168
+ ]);
1169
+ /**
1170
+ * Retrieve a entity given a path and a collection
1171
+ * @param path
1172
+ * @param id
1173
+ * @param collection
1174
+ * @group Firestore
1175
+ */
1176
+ const fetchOne = (0, react.useCallback)(({ path, id, collection }) => {
1177
+ return getAndBuildEntity(path, id, collection?.databaseId);
1178
+ }, [getAndBuildEntity]);
1179
+ /**
1180
+ * Save entity to the specified path. Note that Firestore does not allow
1181
+ * undefined values.
1182
+ * @param path
1183
+ * @param id
1184
+ * @param values
1185
+ * @param schemaId
1186
+ * @param collection
1187
+ * @param status
1188
+ * @group Firestore
1189
+ */
1190
+ const save = (0, react.useCallback)(({ path, id, values: valuesProp, collection, status }) => {
1191
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1192
+ console.debug("1", {
1193
+ path,
1194
+ id,
1195
+ values: valuesProp,
1196
+ collection
1197
+ });
1198
+ const values = cmsToFirestoreModel(valuesProp, (0, _firebase_firestore.getFirestore)(firebaseApp));
1199
+ console.debug("2", {
1200
+ path,
1201
+ id,
1202
+ values: valuesProp,
1203
+ collection
1204
+ });
1205
+ const databaseId = collection?.databaseId;
1206
+ const collectionReference = (0, _firebase_firestore.collection)(databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp), path);
1207
+ console.debug("Saving entity", {
1208
+ path,
1209
+ id,
1210
+ values,
1211
+ databaseId
1212
+ });
1213
+ let documentReference;
1214
+ if (id) documentReference = (0, _firebase_firestore.doc)(collectionReference, String(id));
1215
+ else documentReference = (0, _firebase_firestore.doc)(collectionReference);
1216
+ return (0, _firebase_firestore.setDoc)(documentReference, values, { merge: true }).then(() => {
1217
+ return {
1218
+ ...firestoreToCMSModel(values),
1219
+ id: documentReference.id
1220
+ };
1221
+ }).catch((error) => {
1222
+ console.error("Error saving entity", error);
1223
+ throw error;
1224
+ });
1225
+ }, [firebaseApp]);
1226
+ /**
1227
+ * Delete a entity
1228
+ * @param entity
1229
+ * @param collection
1230
+ * @group Firestore
1231
+ */
1232
+ const deleteOne = (0, react.useCallback)(({ row, collection }) => {
1233
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1234
+ const databaseId = collection?.databaseId;
1235
+ return (0, _firebase_firestore.deleteDoc)((0, _firebase_firestore.doc)(databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp), row.path, String(row.id)));
1236
+ }, [firebaseApp]);
1237
+ /**
1238
+ * Check if the given property is unique in the given collection
1239
+ * @param path Collection path
1240
+ * @param name of the property
1241
+ * @param value
1242
+ * @param property
1243
+ * @param id
1244
+ * @return `true` if there are no other fields besides the given entity
1245
+ * @group Firestore
1246
+ */
1247
+ const checkUniqueField = (0, react.useCallback)(async (path, name, value, id, collection) => {
1248
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1249
+ const databaseId = collection?.databaseId;
1250
+ const firestore = databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp);
1251
+ if (value === void 0 || value === null) return Promise.resolve(true);
1252
+ return (await (0, _firebase_firestore.getDocs)((0, _firebase_firestore.query)((0, _firebase_firestore.collection)(firestore, path), (0, _firebase_firestore.where)(name, "==", cmsToFirestoreModel(value, firestore))))).docs.filter((doc) => doc.id !== id).length === 0;
1253
+ }, [firebaseApp]);
1254
+ const count = (0, react.useCallback)(async ({ path, filter, order, orderBy, collection }) => {
1255
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1256
+ const databaseId = collection?.databaseId;
1257
+ return (await (0, _firebase_firestore.getCountFromServer)(buildQuery(path, filter, orderBy, order, void 0, void 0, databaseId))).data().count;
1258
+ }, [firebaseApp]);
1259
+ const isFilterCombinationValid = (0, react.useCallback)(({ path, collection, filterValues, sortBy }) => {
1260
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1261
+ if (firestoreIndexesBuilder === void 0) return true;
1262
+ const indexes = firestoreIndexesBuilder?.({
1263
+ path,
1264
+ collection
1265
+ });
1266
+ const sortKey = sortBy ? sortBy[0] : void 0;
1267
+ const sortDirection = sortBy ? sortBy[1] : void 0;
1268
+ const values = Object.values(filterValues);
1269
+ const filterKeys = Object.keys(filterValues);
1270
+ const filtersCount = filterKeys.length;
1271
+ if (!sortKey && values.every((v) => v[0] === "==")) return true;
1272
+ if (filtersCount === 1 && (!sortKey || sortKey === filterKeys[0])) return true;
1273
+ if (!indexes && filtersCount > 1) return false;
1274
+ return !!indexes && indexes.filter((compositeIndex) => !sortKey || sortKey in compositeIndex).find((compositeIndex) => Object.entries(filterValues).every(([key, value]) => compositeIndex[key] !== void 0 && (!sortDirection || compositeIndex[key] === sortDirection))) !== void 0;
1275
+ }, [firebaseApp]);
1276
+ return (0, react.useMemo)(() => ({
1069
1277
  key: "firestore",
1070
1278
  currentTime,
1071
1279
  initialised: Boolean(firebaseApp),
1072
- initTextSearch: (0, react.useCallback)(async (props) => {
1073
- console.debug("Init text search controller", searchControllerRef.current, props.path);
1074
- if (!searchControllerRef.current) {
1075
- console.warn("You are trying to use text search, but have not provided a text search controller in `useFirestoreDriver`. You can also set the flag `localTextSearchEnabled` to use local search in `useFirestoreDriver`. Local text search can incur in performance issues and higher costs for large datasets.");
1076
- return false;
1077
- }
1078
- try {
1079
- return searchControllerRef.current.init(props);
1080
- } catch (e) {
1081
- console.error("Error initializing text search controller", e);
1082
- return false;
1083
- }
1084
- }, []),
1085
- /**
1086
- * Fetch entities in a Firestore path
1087
- * @param path
1088
- * @param collection
1089
- * @param filter
1090
- * @param limit
1091
- * @param startAfter
1092
- * @param searchString
1093
- * @param orderBy
1094
- * @param order
1095
- * @return Function to cancel subscription
1096
- * @see useCollectionFetch if you need this functionality implemented as a hook
1097
- * @group Firestore
1098
- */
1099
- fetchCollection: (0, react.useCallback)(async ({ path, filter, limit, startAfter, searchString, orderBy, order, collection }) => {
1100
- const databaseId = collection?.databaseId;
1101
- const resolvedPath = path;
1102
- console.debug("Fetching collection", {
1103
- path,
1104
- limit,
1105
- filter,
1106
- startAfter,
1107
- orderBy,
1108
- order
1109
- });
1110
- return (await (0, _firebase_firestore.getDocs)(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId))).docs.map((doc) => createEntityFromDocument(doc, databaseId));
1111
- }, [buildQuery]),
1112
- /**
1113
- * Listen to a entities in a given path
1114
- * @param path
1115
- * @param collection
1116
- * @param onError
1117
- * @param filter
1118
- * @param limit
1119
- * @param startAfter
1120
- * @param searchString
1121
- * @param orderBy
1122
- * @param order
1123
- * @param onUpdate
1124
- * @return Function to cancel subscription
1125
- * @see useCollectionFetch if you need this functionality implemented as a hook
1126
- * @group Firestore
1127
- */
1128
- listenCollection: (0, react.useCallback)(({ path, filter, limit, startAfter, searchString, orderBy, order, onUpdate, onError, collection }) => {
1129
- console.debug("Listening collection", {
1130
- path,
1131
- searchString,
1132
- limit,
1133
- filter,
1134
- startAfter,
1135
- orderBy,
1136
- order,
1137
- collection
1138
- });
1139
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1140
- const databaseId = collection?.databaseId;
1141
- if (searchString) return performTextSearch({
1142
- path,
1143
- searchString,
1144
- onUpdate,
1145
- databaseId
1146
- });
1147
- const resolvedPath = path;
1148
- console.debug("Resolved path for listening", {
1149
- path,
1150
- resolvedPath
1151
- });
1152
- return (0, _firebase_firestore.onSnapshot)(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId), {
1153
- next: (snapshot) => {
1154
- if (!searchString) onUpdate(snapshot.docs.map((doc) => createEntityFromDocument(doc, databaseId)));
1155
- },
1156
- error: onError
1157
- });
1158
- }, [
1159
- buildQuery,
1160
- firebaseApp,
1161
- performTextSearch
1162
- ]),
1163
- /**
1164
- * Retrieve an entity given a path and a collection
1165
- * @param path
1166
- * @param entityId
1167
- * @param collection
1168
- * @group Firestore
1169
- */
1170
- fetchEntity: (0, react.useCallback)(({ path, entityId, collection }) => {
1171
- return getAndBuildEntity(path, entityId, collection?.databaseId);
1172
- }, [getAndBuildEntity]),
1173
- /**
1174
- *
1175
- * @param path
1176
- * @param entityId
1177
- * @param collection
1178
- * @param onUpdate
1179
- * @param onError
1180
- * @return Function to cancel subscription
1181
- * @group Firestore
1182
- */
1183
- listenEntity,
1184
- /**
1185
- * Save entity to the specified path. Note that Firestore does not allow
1186
- * undefined values.
1187
- * @param path
1188
- * @param entityId
1189
- * @param values
1190
- * @param schemaId
1191
- * @param collection
1192
- * @param status
1193
- * @group Firestore
1194
- */
1195
- saveEntity: (0, react.useCallback)(({ path, entityId, values: valuesProp, collection, status }) => {
1196
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1197
- console.debug("1", {
1198
- path,
1199
- entityId,
1200
- values: valuesProp,
1201
- collection
1202
- });
1203
- const values = cmsToFirestoreModel(valuesProp, (0, _firebase_firestore.getFirestore)(firebaseApp));
1204
- console.debug("2", {
1205
- path,
1206
- entityId,
1207
- values: valuesProp,
1208
- collection
1209
- });
1210
- const databaseId = collection?.databaseId;
1211
- const collectionReference = (0, _firebase_firestore.collection)(databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp), path);
1212
- console.debug("Saving entity", {
1213
- path,
1214
- entityId,
1215
- values,
1216
- databaseId
1217
- });
1218
- let documentReference;
1219
- if (entityId) documentReference = (0, _firebase_firestore.doc)(collectionReference, String(entityId));
1220
- else documentReference = (0, _firebase_firestore.doc)(collectionReference);
1221
- return (0, _firebase_firestore.setDoc)(documentReference, values, { merge: true }).then(() => {
1222
- return {
1223
- id: documentReference.id,
1224
- path,
1225
- values: firestoreToCMSModel(values)
1226
- };
1227
- }).catch((error) => {
1228
- console.error("Error saving entity", error);
1229
- throw error;
1230
- });
1231
- }, [firebaseApp]),
1232
- /**
1233
- * Delete an entity
1234
- * @param entity
1235
- * @param collection
1236
- * @group Firestore
1237
- */
1238
- deleteEntity: (0, react.useCallback)(({ entity }) => {
1239
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1240
- const databaseId = entity.databaseId;
1241
- return (0, _firebase_firestore.deleteDoc)((0, _firebase_firestore.doc)(databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp), entity.path, String(entity.id)));
1242
- }, [firebaseApp]),
1243
- /**
1244
- * Check if the given property is unique in the given collection
1245
- * @param path Collection path
1246
- * @param name of the property
1247
- * @param value
1248
- * @param property
1249
- * @param entityId
1250
- * @return `true` if there are no other fields besides the given entity
1251
- * @group Firestore
1252
- */
1253
- checkUniqueField: (0, react.useCallback)(async (path, name, value, entityId, collection) => {
1254
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1255
- const databaseId = collection?.databaseId;
1256
- const firestore = databaseId ? (0, _firebase_firestore.getFirestore)(firebaseApp, databaseId) : (0, _firebase_firestore.getFirestore)(firebaseApp);
1257
- if (value === void 0 || value === null) return Promise.resolve(true);
1258
- return (await (0, _firebase_firestore.getDocs)((0, _firebase_firestore.query)((0, _firebase_firestore.collection)(firestore, path), (0, _firebase_firestore.where)(name, "==", cmsToFirestoreModel(value, firestore))))).docs.filter((doc) => doc.id !== entityId).length === 0;
1259
- }, [firebaseApp]),
1260
- countEntities: (0, react.useCallback)(async ({ path, filter, order, orderBy, collection }) => {
1261
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1262
- const databaseId = collection?.databaseId;
1263
- return (await (0, _firebase_firestore.getCountFromServer)(buildQuery(path, filter, orderBy, order, void 0, void 0, databaseId))).data().count;
1264
- }, [firebaseApp]),
1265
- isFilterCombinationValid: (0, react.useCallback)(({ path, collection, filterValues, sortBy }) => {
1266
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1267
- if (firestoreIndexesBuilder === void 0) return true;
1268
- const indexes = firestoreIndexesBuilder?.({
1269
- path,
1270
- collection
1271
- });
1272
- const sortKey = sortBy ? sortBy[0] : void 0;
1273
- const sortDirection = sortBy ? sortBy[1] : void 0;
1274
- const values = Object.values(filterValues);
1275
- const filterKeys = Object.keys(filterValues);
1276
- const filtersCount = filterKeys.length;
1277
- if (!sortKey && values.every((v) => v[0] === "==")) return true;
1278
- if (filtersCount === 1 && (!sortKey || sortKey === filterKeys[0])) return true;
1279
- if (!indexes && filtersCount > 1) return false;
1280
- return !!indexes && indexes.filter((compositeIndex) => !sortKey || sortKey in compositeIndex).find((compositeIndex) => Object.entries(filterValues).every(([key, value]) => compositeIndex[key] !== void 0 && (!sortDirection || compositeIndex[key] === sortDirection))) !== void 0;
1281
- }, [firebaseApp])
1282
- };
1280
+ initTextSearch,
1281
+ fetchCollection,
1282
+ listenCollection,
1283
+ fetchOne,
1284
+ listenOne,
1285
+ save,
1286
+ delete: deleteOne,
1287
+ checkUniqueField,
1288
+ count,
1289
+ isFilterCombinationValid
1290
+ }), [
1291
+ firebaseApp,
1292
+ initTextSearch,
1293
+ fetchCollection,
1294
+ listenCollection,
1295
+ fetchOne,
1296
+ listenOne,
1297
+ save,
1298
+ deleteOne,
1299
+ checkUniqueField,
1300
+ count,
1301
+ isFilterCombinationValid
1302
+ ]);
1283
1303
  }
1284
- var createEntityFromDocument = (docSnap, databaseId) => {
1285
- const values = firestoreToCMSModel(docSnap.data());
1286
- const path = getCMSPathFromFirestorePath(docSnap.ref.path);
1304
+ var createRowFromDocument = (docSnap) => {
1287
1305
  return {
1288
- id: docSnap.id,
1289
- path,
1290
- values,
1291
- databaseId
1306
+ ...firestoreToCMSModel(docSnap.data()),
1307
+ id: docSnap.id
1292
1308
  };
1293
1309
  };
1294
1310
  /**
@@ -1403,70 +1419,65 @@
1403
1419
  let dbQuery = (0, _firebase_database.query)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), path));
1404
1420
  if (startAfter !== void 0) dbQuery = (0, _firebase_database.query)(dbQuery, (0, _firebase_database.orderByKey)(), (0, _firebase_database.startAt)(String(startAfter)));
1405
1421
  if (limit !== void 0) dbQuery = (0, _firebase_database.query)(dbQuery, (0, _firebase_database.limitToFirst)(limit));
1406
- const snapshot = await (0, _firebase_database.get)(dbQuery);
1407
- if (snapshot.exists()) return Object.entries(snapshot.val()).map(([id, values]) => ({
1408
- id,
1409
- path,
1410
- values: delegateToCMSModel(values)
1422
+ const entity = await (0, _firebase_database.get)(dbQuery);
1423
+ if (entity.exists()) return Object.entries(entity.val()).map(([id, values]) => ({
1424
+ ...delegateToCMSModel(values),
1425
+ id
1411
1426
  }));
1412
1427
  return [];
1413
1428
  }, [firebaseApp]),
1414
1429
  listenCollection: (0, react.useCallback)(({ path, onUpdate }) => {
1415
1430
  if (!firebaseApp) throw new Error("Firebase app not provided");
1416
- const unsubscribe = (0, _firebase_database.onValue)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), path), (snapshot) => {
1417
- if (snapshot.exists()) onUpdate(Object.entries(snapshot.val()).map(([id, values]) => ({
1418
- id,
1419
- path,
1420
- values: delegateToCMSModel(values)
1431
+ const unsubscribe = (0, _firebase_database.onValue)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), path), (entity) => {
1432
+ if (entity.exists()) onUpdate(Object.entries(entity.val()).map(([id, values]) => ({
1433
+ ...delegateToCMSModel(values),
1434
+ id
1421
1435
  })));
1422
1436
  else onUpdate([]);
1423
1437
  });
1424
1438
  return () => unsubscribe();
1425
1439
  }, [firebaseApp]),
1426
- fetchEntity: (0, react.useCallback)(async ({ path, entityId }) => {
1440
+ fetchOne: (0, react.useCallback)(async ({ path, id }) => {
1427
1441
  if (!firebaseApp) throw new Error("Firebase app not provided");
1428
- const snapshot = await (0, _firebase_database.get)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), `${path}/${entityId}`));
1429
- if (snapshot.exists()) return {
1430
- id: entityId,
1431
- path,
1432
- values: delegateToCMSModel(snapshot.val())
1442
+ const entity = await (0, _firebase_database.get)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), `${path}/${id}`));
1443
+ if (entity.exists()) return {
1444
+ ...delegateToCMSModel(entity.val()),
1445
+ id
1433
1446
  };
1434
1447
  }, [firebaseApp]),
1435
- listenEntity: (0, react.useCallback)(({ path, entityId, onUpdate, onError }) => {
1448
+ listenOne: (0, react.useCallback)(({ path, id, onUpdate, onError }) => {
1436
1449
  if (!firebaseApp) throw new Error("Firebase app not provided");
1437
- const unsubscribe = (0, _firebase_database.onValue)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), `${path}/${entityId}`), (snapshot) => {
1438
- if (snapshot.exists()) onUpdate({
1439
- id: entityId,
1440
- path,
1441
- values: delegateToCMSModel(snapshot.val())
1450
+ const unsubscribe = (0, _firebase_database.onValue)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), `${path}/${id}`), (entity) => {
1451
+ if (entity.exists()) onUpdate({
1452
+ ...delegateToCMSModel(entity.val()),
1453
+ id
1442
1454
  });
1443
1455
  else onError?.(/* @__PURE__ */ new Error("Entity does not exist"));
1444
1456
  });
1445
1457
  return () => unsubscribe();
1446
1458
  }, [firebaseApp]),
1447
- saveEntity: (0, react.useCallback)(async ({ path, entityId, values }) => {
1459
+ save: (0, react.useCallback)(async ({ path, id, values }) => {
1448
1460
  if (!firebaseApp) throw new Error("Firebase app not provided");
1449
1461
  const database = (0, _firebase_database.getDatabase)(firebaseApp);
1450
- const finalId = entityId ?? (0, _firebase_database.push)((0, _firebase_database.ref)(database, path)).key;
1462
+ const finalId = id ?? (0, _firebase_database.push)((0, _firebase_database.ref)(database, path)).key;
1451
1463
  if (!finalId) throw new Error("Could not generate a new id");
1452
1464
  const transformedValues = cmsToRTDBModel(values, database);
1453
1465
  await (0, _firebase_database.set)((0, _firebase_database.ref)(database, `${path}/${finalId}`), transformedValues);
1454
1466
  return {
1455
- id: finalId,
1456
- path,
1457
- values
1467
+ ...values,
1468
+ id: finalId
1458
1469
  };
1459
1470
  }, [firebaseApp]),
1460
- deleteEntity: (0, react.useCallback)(async ({ entity }) => {
1471
+ delete: (0, react.useCallback)(async ({ row }) => {
1461
1472
  if (!firebaseApp) throw new Error("Firebase app not provided");
1462
- await (0, _firebase_database.remove)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), `${entity.path}/${entity.id}`));
1473
+ await (0, _firebase_database.remove)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), `${row.path}/${row.id}`));
1463
1474
  }, [firebaseApp]),
1464
- checkUniqueField: (0, react.useCallback)(async (slug, name, value, entityId) => {
1475
+ checkUniqueField: (0, react.useCallback)(async (slug, name, value, id) => {
1465
1476
  if (!firebaseApp) throw new Error("Firebase app not provided");
1466
- const snapshot = await (0, _firebase_database.get)((0, _firebase_database.query)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), slug), (0, _firebase_database.orderByChild)(name), (0, _firebase_database.startAt)(value), (0, _firebase_database.limitToFirst)(1)));
1467
- if (!snapshot.exists()) return true;
1468
- const [key, entityValue] = Object.entries(snapshot.val())[0];
1469
- if (entityValue && typeof entityValue === "object" && entityValue[name] === value && key === entityId) return true;
1477
+ const entity = await (0, _firebase_database.get)((0, _firebase_database.query)((0, _firebase_database.ref)((0, _firebase_database.getDatabase)(firebaseApp), slug), (0, _firebase_database.orderByChild)(name), (0, _firebase_database.startAt)(value), (0, _firebase_database.limitToFirst)(1)));
1478
+ if (!entity.exists()) return true;
1479
+ const [key, entityValue] = Object.entries(entity.val())[0];
1480
+ if (entityValue && typeof entityValue === "object" && entityValue[name] === value && key === id) return true;
1470
1481
  return false;
1471
1482
  }, [firebaseApp]),
1472
1483
  isFilterCombinationValid: (0, react.useCallback)(({ path, filter, sortBy }) => {
@@ -1556,11 +1567,11 @@
1556
1567
  setRolesLoading(true);
1557
1568
  return dataSourceDelegate.listenCollection?.({
1558
1569
  path: rolesPath,
1559
- onUpdate(entities) {
1570
+ onUpdate(rows) {
1560
1571
  setRolesError(void 0);
1561
- console.debug("Updating roles", entities);
1572
+ console.debug("Updating roles", rows);
1562
1573
  try {
1563
- const newRoles = entityToRoles(entities);
1574
+ const newRoles = rowsToRoles(rows);
1564
1575
  if (!(0, fast_equals.deepEqual)(newRoles, roles)) setRoles(newRoles);
1565
1576
  } catch (e) {
1566
1577
  setRoles([]);
@@ -1590,11 +1601,11 @@
1590
1601
  setUsersLoading(true);
1591
1602
  return dataSourceDelegate.listenCollection?.({
1592
1603
  path: usersPath,
1593
- onUpdate(entities) {
1594
- console.debug("Updating users", entities);
1604
+ onUpdate(rows) {
1605
+ console.debug("Updating users", rows);
1595
1606
  setUsersError(void 0);
1596
1607
  try {
1597
- setUsersWithRoleIds(entitiesToUsers(entities));
1608
+ setUsersWithRoleIds(rowsToUsers(rows));
1598
1609
  } catch (e) {
1599
1610
  setUsersWithRoleIds([]);
1600
1611
  console.error("Error loading users", e);
@@ -1629,21 +1640,21 @@
1629
1640
  ...userExists ? {} : { created_on: /* @__PURE__ */ new Date() }
1630
1641
  };
1631
1642
  if (userExists && userExists.uid !== user.uid) {
1632
- const entity = {
1643
+ const row = {
1633
1644
  values: {},
1634
1645
  path: usersPath,
1635
1646
  id: userExists.uid
1636
1647
  };
1637
- await dataSourceDelegate.deleteEntity({ entity }).then(() => {
1648
+ await dataSourceDelegate.delete({ row }).then(() => {
1638
1649
  console.debug("Deleted previous user", userExists);
1639
1650
  }).catch((e) => {
1640
1651
  console.error("Error deleting user", e);
1641
1652
  });
1642
1653
  }
1643
- return dataSourceDelegate.saveEntity({
1654
+ return dataSourceDelegate.save({
1644
1655
  status: "existing",
1645
1656
  path: usersPath,
1646
- entityId: email,
1657
+ id: email,
1647
1658
  values: (0, _rebasepro_utils.removeUndefined)(data)
1648
1659
  }).then(() => user);
1649
1660
  }, [usersPath, dataSourceDelegate?.initialised]);
@@ -1652,10 +1663,10 @@
1652
1663
  if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
1653
1664
  console.debug("Persisting role", role);
1654
1665
  const { id, ...roleData } = role;
1655
- return dataSourceDelegate.saveEntity({
1666
+ return dataSourceDelegate.save({
1656
1667
  status: "existing",
1657
1668
  path: rolesPath,
1658
- entityId: id,
1669
+ id,
1659
1670
  values: (0, _rebasepro_utils.removeUndefined)(roleData)
1660
1671
  }).then(() => {});
1661
1672
  }, [rolesPath, dataSourceDelegate?.initialised]);
@@ -1664,24 +1675,24 @@
1664
1675
  if (!usersPath) throw Error("useBuildUserManagement Firestore not initialised");
1665
1676
  console.debug("Deleting", user);
1666
1677
  const { uid } = user;
1667
- const entity = {
1678
+ const row = {
1668
1679
  path: usersPath,
1669
1680
  id: uid,
1670
1681
  values: {}
1671
1682
  };
1672
- await dataSourceDelegate.deleteEntity({ entity });
1683
+ await dataSourceDelegate.delete({ row });
1673
1684
  }, [usersPath, dataSourceDelegate?.initialised]);
1674
1685
  const deleteRole = (0, react.useCallback)(async (role) => {
1675
1686
  if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
1676
1687
  if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
1677
1688
  console.debug("Deleting", role);
1678
1689
  const { id } = role;
1679
- const entity = {
1690
+ const row = {
1680
1691
  path: rolesPath,
1681
1692
  id,
1682
1693
  values: {}
1683
1694
  };
1684
- await dataSourceDelegate.deleteEntity({ entity });
1695
+ await dataSourceDelegate.delete({ row });
1685
1696
  }, [rolesPath, dataSourceDelegate?.initialised]);
1686
1697
  const defineRolesFor = (0, react.useCallback)((user) => {
1687
1698
  if (!usersWithRoleIds) throw Error("Users not loaded");
@@ -1757,22 +1768,21 @@
1757
1768
  } : null
1758
1769
  };
1759
1770
  }
1760
- var entitiesToUsers = (docs) => {
1761
- return docs.map((doc) => {
1762
- const data = doc.values;
1763
- const record = data;
1771
+ var rowsToUsers = (rows) => {
1772
+ return rows.map((row) => {
1773
+ const { id, ...data } = row;
1764
1774
  return {
1765
1775
  ...data,
1766
- uid: doc.id,
1767
- created_on: record.created_on,
1768
- updated_on: record.updated_on
1776
+ uid: id,
1777
+ created_on: data.created_on,
1778
+ updated_on: data.updated_on
1769
1779
  };
1770
1780
  });
1771
1781
  };
1772
- var entityToRoles = (entities) => {
1773
- return entities.map((doc) => ({
1774
- id: doc.id,
1775
- ...doc.values
1782
+ var rowsToRoles = (rows) => {
1783
+ return rows.map((row) => ({
1784
+ ...row,
1785
+ id: row.id
1776
1786
  }));
1777
1787
  };
1778
1788
  //#endregion
@@ -2600,7 +2610,12 @@
2600
2610
  authController,
2601
2611
  userConfigPersistence,
2602
2612
  dateTimeFormat,
2603
- driver: firestoreDelegate,
2613
+ dataSources: [{
2614
+ key: _rebasepro_types.DEFAULT_DATA_SOURCE_KEY,
2615
+ engine: "firestore",
2616
+ transport: "direct",
2617
+ driver: firestoreDelegate
2618
+ }],
2604
2619
  storageSource,
2605
2620
  entityLinkBuilder: ({ entity }) => `https://console.firebase.google.com/project/${firebaseApp.options.projectId}/firestore/data/${entity.path}/${entity.id}`,
2606
2621
  locale,
@@ -2626,7 +2641,7 @@
2626
2641
  to: urlController.buildUrlCollectionPath(firstCollectionEntry.id),
2627
2642
  replace: true
2628
2643
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CenteredView, { children: "No home page or collections provided." });
2629
- component = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_admin.SideEntityProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_core.RebaseRoutes, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_router_dom.Route, {
2644
+ component = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_admin.SidePanelProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_core.RebaseRoutes, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_router_dom.Route, {
2630
2645
  element: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_admin.Scaffold, {
2631
2646
  logo: usedLogo,
2632
2647
  autoOpenDrawer,