@rebasepro/client-firebase 0.7.0 → 0.8.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.es.js CHANGED
@@ -1035,221 +1035,232 @@ function useFirestoreDriver({ firebaseApp, textSearchControllerBuilder, firestor
1035
1035
  subscriptions.forEach((p) => p());
1036
1036
  };
1037
1037
  }, [firebaseApp, listenEntity]);
1038
- return {
1038
+ const initTextSearch = useCallback(async (props) => {
1039
+ console.debug("Init text search controller", searchControllerRef.current, props.path);
1040
+ if (!searchControllerRef.current) {
1041
+ 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.");
1042
+ return false;
1043
+ }
1044
+ try {
1045
+ return searchControllerRef.current.init(props);
1046
+ } catch (e) {
1047
+ console.error("Error initializing text search controller", e);
1048
+ return false;
1049
+ }
1050
+ }, []);
1051
+ /**
1052
+ * Fetch entities in a Firestore path
1053
+ * @param path
1054
+ * @param collection
1055
+ * @param filter
1056
+ * @param limit
1057
+ * @param startAfter
1058
+ * @param searchString
1059
+ * @param orderBy
1060
+ * @param order
1061
+ * @return Function to cancel subscription
1062
+ * @see useCollectionFetch if you need this functionality implemented as a hook
1063
+ * @group Firestore
1064
+ */
1065
+ const fetchCollection = useCallback(async ({ path, filter, limit, startAfter, searchString, orderBy, order, collection }) => {
1066
+ const databaseId = collection?.databaseId;
1067
+ const resolvedPath = path;
1068
+ console.debug("Fetching collection", {
1069
+ path,
1070
+ limit,
1071
+ filter,
1072
+ startAfter,
1073
+ orderBy,
1074
+ order
1075
+ });
1076
+ return (await getDocs(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId))).docs.map((doc) => createEntityFromDocument(doc, databaseId));
1077
+ }, [buildQuery]);
1078
+ /**
1079
+ * Listen to a entities in a given path
1080
+ * @param path
1081
+ * @param collection
1082
+ * @param onError
1083
+ * @param filter
1084
+ * @param limit
1085
+ * @param startAfter
1086
+ * @param searchString
1087
+ * @param orderBy
1088
+ * @param order
1089
+ * @param onUpdate
1090
+ * @return Function to cancel subscription
1091
+ * @see useCollectionFetch if you need this functionality implemented as a hook
1092
+ * @group Firestore
1093
+ */
1094
+ const listenCollection = useCallback(({ path, filter, limit, startAfter, searchString, orderBy, order, onUpdate, onError, collection }) => {
1095
+ console.debug("Listening collection", {
1096
+ path,
1097
+ searchString,
1098
+ limit,
1099
+ filter,
1100
+ startAfter,
1101
+ orderBy,
1102
+ order,
1103
+ collection
1104
+ });
1105
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1106
+ const databaseId = collection?.databaseId;
1107
+ if (searchString) return performTextSearch({
1108
+ path,
1109
+ searchString,
1110
+ onUpdate,
1111
+ databaseId
1112
+ });
1113
+ const resolvedPath = path;
1114
+ console.debug("Resolved path for listening", {
1115
+ path,
1116
+ resolvedPath
1117
+ });
1118
+ return onSnapshot(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId), {
1119
+ next: (snapshot) => {
1120
+ if (!searchString) onUpdate(snapshot.docs.map((doc) => createEntityFromDocument(doc, databaseId)));
1121
+ },
1122
+ error: onError
1123
+ });
1124
+ }, [
1125
+ buildQuery,
1126
+ firebaseApp,
1127
+ performTextSearch
1128
+ ]);
1129
+ /**
1130
+ * Retrieve an entity given a path and a collection
1131
+ * @param path
1132
+ * @param entityId
1133
+ * @param collection
1134
+ * @group Firestore
1135
+ */
1136
+ const fetchEntity = useCallback(({ path, entityId, collection }) => {
1137
+ return getAndBuildEntity(path, entityId, collection?.databaseId);
1138
+ }, [getAndBuildEntity]);
1139
+ /**
1140
+ * Save entity to the specified path. Note that Firestore does not allow
1141
+ * undefined values.
1142
+ * @param path
1143
+ * @param entityId
1144
+ * @param values
1145
+ * @param schemaId
1146
+ * @param collection
1147
+ * @param status
1148
+ * @group Firestore
1149
+ */
1150
+ const saveEntity = useCallback(({ path, entityId, values: valuesProp, collection: collection$1, status }) => {
1151
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1152
+ console.debug("1", {
1153
+ path,
1154
+ entityId,
1155
+ values: valuesProp,
1156
+ collection: collection$1
1157
+ });
1158
+ const values = cmsToFirestoreModel(valuesProp, getFirestore(firebaseApp));
1159
+ console.debug("2", {
1160
+ path,
1161
+ entityId,
1162
+ values: valuesProp,
1163
+ collection: collection$1
1164
+ });
1165
+ const databaseId = collection$1?.databaseId;
1166
+ const collectionReference = collection(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), path);
1167
+ console.debug("Saving entity", {
1168
+ path,
1169
+ entityId,
1170
+ values,
1171
+ databaseId
1172
+ });
1173
+ let documentReference;
1174
+ if (entityId) documentReference = doc(collectionReference, String(entityId));
1175
+ else documentReference = doc(collectionReference);
1176
+ return setDoc(documentReference, values, { merge: true }).then(() => {
1177
+ return {
1178
+ id: documentReference.id,
1179
+ path,
1180
+ values: firestoreToCMSModel(values)
1181
+ };
1182
+ }).catch((error) => {
1183
+ console.error("Error saving entity", error);
1184
+ throw error;
1185
+ });
1186
+ }, [firebaseApp]);
1187
+ /**
1188
+ * Delete an entity
1189
+ * @param entity
1190
+ * @param collection
1191
+ * @group Firestore
1192
+ */
1193
+ const deleteEntity = useCallback(({ entity }) => {
1194
+ 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)));
1197
+ }, [firebaseApp]);
1198
+ /**
1199
+ * Check if the given property is unique in the given collection
1200
+ * @param path Collection path
1201
+ * @param name of the property
1202
+ * @param value
1203
+ * @param property
1204
+ * @param entityId
1205
+ * @return `true` if there are no other fields besides the given entity
1206
+ * @group Firestore
1207
+ */
1208
+ const checkUniqueField = useCallback(async (path, name, value, entityId, collection$2) => {
1209
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1210
+ const databaseId = collection$2?.databaseId;
1211
+ const firestore = databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp);
1212
+ 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;
1214
+ }, [firebaseApp]);
1215
+ const countEntities = useCallback(async ({ path, filter, order, orderBy, collection }) => {
1216
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1217
+ const databaseId = collection?.databaseId;
1218
+ return (await getCountFromServer(buildQuery(path, filter, orderBy, order, void 0, void 0, databaseId))).data().count;
1219
+ }, [firebaseApp]);
1220
+ const isFilterCombinationValid = useCallback(({ path, collection, filterValues, sortBy }) => {
1221
+ if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1222
+ if (firestoreIndexesBuilder === void 0) return true;
1223
+ const indexes = firestoreIndexesBuilder?.({
1224
+ path,
1225
+ collection
1226
+ });
1227
+ const sortKey = sortBy ? sortBy[0] : void 0;
1228
+ const sortDirection = sortBy ? sortBy[1] : void 0;
1229
+ const values = Object.values(filterValues);
1230
+ const filterKeys = Object.keys(filterValues);
1231
+ const filtersCount = filterKeys.length;
1232
+ if (!sortKey && values.every((v) => v[0] === "==")) return true;
1233
+ if (filtersCount === 1 && (!sortKey || sortKey === filterKeys[0])) return true;
1234
+ if (!indexes && filtersCount > 1) return false;
1235
+ 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;
1236
+ }, [firebaseApp]);
1237
+ return useMemo(() => ({
1039
1238
  key: "firestore",
1040
1239
  currentTime,
1041
1240
  initialised: Boolean(firebaseApp),
1042
- initTextSearch: useCallback(async (props) => {
1043
- console.debug("Init text search controller", searchControllerRef.current, props.path);
1044
- if (!searchControllerRef.current) {
1045
- 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.");
1046
- return false;
1047
- }
1048
- try {
1049
- return searchControllerRef.current.init(props);
1050
- } catch (e) {
1051
- console.error("Error initializing text search controller", e);
1052
- return false;
1053
- }
1054
- }, []),
1055
- /**
1056
- * Fetch entities in a Firestore path
1057
- * @param path
1058
- * @param collection
1059
- * @param filter
1060
- * @param limit
1061
- * @param startAfter
1062
- * @param searchString
1063
- * @param orderBy
1064
- * @param order
1065
- * @return Function to cancel subscription
1066
- * @see useCollectionFetch if you need this functionality implemented as a hook
1067
- * @group Firestore
1068
- */
1069
- fetchCollection: useCallback(async ({ path, filter, limit, startAfter, searchString, orderBy, order, collection }) => {
1070
- const databaseId = collection?.databaseId;
1071
- const resolvedPath = path;
1072
- console.debug("Fetching collection", {
1073
- path,
1074
- limit,
1075
- filter,
1076
- startAfter,
1077
- orderBy,
1078
- order
1079
- });
1080
- return (await getDocs(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId))).docs.map((doc) => createEntityFromDocument(doc, databaseId));
1081
- }, [buildQuery]),
1082
- /**
1083
- * Listen to a entities in a given path
1084
- * @param path
1085
- * @param collection
1086
- * @param onError
1087
- * @param filter
1088
- * @param limit
1089
- * @param startAfter
1090
- * @param searchString
1091
- * @param orderBy
1092
- * @param order
1093
- * @param onUpdate
1094
- * @return Function to cancel subscription
1095
- * @see useCollectionFetch if you need this functionality implemented as a hook
1096
- * @group Firestore
1097
- */
1098
- listenCollection: useCallback(({ path, filter, limit, startAfter, searchString, orderBy, order, onUpdate, onError, collection }) => {
1099
- console.debug("Listening collection", {
1100
- path,
1101
- searchString,
1102
- limit,
1103
- filter,
1104
- startAfter,
1105
- orderBy,
1106
- order,
1107
- collection
1108
- });
1109
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1110
- const databaseId = collection?.databaseId;
1111
- if (searchString) return performTextSearch({
1112
- path,
1113
- searchString,
1114
- onUpdate,
1115
- databaseId
1116
- });
1117
- const resolvedPath = path;
1118
- console.debug("Resolved path for listening", {
1119
- path,
1120
- resolvedPath
1121
- });
1122
- return onSnapshot(buildQuery(resolvedPath, filter, orderBy, order, startAfter, limit, databaseId), {
1123
- next: (snapshot) => {
1124
- if (!searchString) onUpdate(snapshot.docs.map((doc) => createEntityFromDocument(doc, databaseId)));
1125
- },
1126
- error: onError
1127
- });
1128
- }, [
1129
- buildQuery,
1130
- firebaseApp,
1131
- performTextSearch
1132
- ]),
1133
- /**
1134
- * Retrieve an entity given a path and a collection
1135
- * @param path
1136
- * @param entityId
1137
- * @param collection
1138
- * @group Firestore
1139
- */
1140
- fetchEntity: useCallback(({ path, entityId, collection }) => {
1141
- return getAndBuildEntity(path, entityId, collection?.databaseId);
1142
- }, [getAndBuildEntity]),
1143
- /**
1144
- *
1145
- * @param path
1146
- * @param entityId
1147
- * @param collection
1148
- * @param onUpdate
1149
- * @param onError
1150
- * @return Function to cancel subscription
1151
- * @group Firestore
1152
- */
1241
+ initTextSearch,
1242
+ fetchCollection,
1243
+ listenCollection,
1244
+ fetchEntity,
1153
1245
  listenEntity,
1154
- /**
1155
- * Save entity to the specified path. Note that Firestore does not allow
1156
- * undefined values.
1157
- * @param path
1158
- * @param entityId
1159
- * @param values
1160
- * @param schemaId
1161
- * @param collection
1162
- * @param status
1163
- * @group Firestore
1164
- */
1165
- saveEntity: useCallback(({ path, entityId, values: valuesProp, collection: collection$1, status }) => {
1166
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1167
- console.debug("1", {
1168
- path,
1169
- entityId,
1170
- values: valuesProp,
1171
- collection: collection$1
1172
- });
1173
- const values = cmsToFirestoreModel(valuesProp, getFirestore(firebaseApp));
1174
- console.debug("2", {
1175
- path,
1176
- entityId,
1177
- values: valuesProp,
1178
- collection: collection$1
1179
- });
1180
- const databaseId = collection$1?.databaseId;
1181
- const collectionReference = collection(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), path);
1182
- console.debug("Saving entity", {
1183
- path,
1184
- entityId,
1185
- values,
1186
- databaseId
1187
- });
1188
- let documentReference;
1189
- if (entityId) documentReference = doc(collectionReference, String(entityId));
1190
- else documentReference = doc(collectionReference);
1191
- return setDoc(documentReference, values, { merge: true }).then(() => {
1192
- return {
1193
- id: documentReference.id,
1194
- path,
1195
- values: firestoreToCMSModel(values)
1196
- };
1197
- }).catch((error) => {
1198
- console.error("Error saving entity", error);
1199
- throw error;
1200
- });
1201
- }, [firebaseApp]),
1202
- /**
1203
- * Delete an entity
1204
- * @param entity
1205
- * @param collection
1206
- * @group Firestore
1207
- */
1208
- deleteEntity: useCallback(({ entity }) => {
1209
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1210
- const databaseId = entity.databaseId;
1211
- return deleteDoc(doc(databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp), entity.path, String(entity.id)));
1212
- }, [firebaseApp]),
1213
- /**
1214
- * Check if the given property is unique in the given collection
1215
- * @param path Collection path
1216
- * @param name of the property
1217
- * @param value
1218
- * @param property
1219
- * @param entityId
1220
- * @return `true` if there are no other fields besides the given entity
1221
- * @group Firestore
1222
- */
1223
- checkUniqueField: useCallback(async (path, name, value, entityId, collection$2) => {
1224
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1225
- const databaseId = collection$2?.databaseId;
1226
- const firestore = databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp);
1227
- if (value === void 0 || value === null) return Promise.resolve(true);
1228
- return (await getDocs(query(collection(firestore, path), where(name, "==", cmsToFirestoreModel(value, firestore))))).docs.filter((doc) => doc.id !== entityId).length === 0;
1229
- }, [firebaseApp]),
1230
- countEntities: useCallback(async ({ path, filter, order, orderBy, collection }) => {
1231
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1232
- const databaseId = collection?.databaseId;
1233
- return (await getCountFromServer(buildQuery(path, filter, orderBy, order, void 0, void 0, databaseId))).data().count;
1234
- }, [firebaseApp]),
1235
- isFilterCombinationValid: useCallback(({ path, collection, filterValues, sortBy }) => {
1236
- if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
1237
- if (firestoreIndexesBuilder === void 0) return true;
1238
- const indexes = firestoreIndexesBuilder?.({
1239
- path,
1240
- collection
1241
- });
1242
- const sortKey = sortBy ? sortBy[0] : void 0;
1243
- const sortDirection = sortBy ? sortBy[1] : void 0;
1244
- const values = Object.values(filterValues);
1245
- const filterKeys = Object.keys(filterValues);
1246
- const filtersCount = filterKeys.length;
1247
- if (!sortKey && values.every((v) => v[0] === "==")) return true;
1248
- if (filtersCount === 1 && (!sortKey || sortKey === filterKeys[0])) return true;
1249
- if (!indexes && filtersCount > 1) return false;
1250
- 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;
1251
- }, [firebaseApp])
1252
- };
1246
+ saveEntity,
1247
+ deleteEntity,
1248
+ checkUniqueField,
1249
+ countEntities,
1250
+ isFilterCombinationValid
1251
+ }), [
1252
+ firebaseApp,
1253
+ initTextSearch,
1254
+ fetchCollection,
1255
+ listenCollection,
1256
+ fetchEntity,
1257
+ listenEntity,
1258
+ saveEntity,
1259
+ deleteEntity,
1260
+ checkUniqueField,
1261
+ countEntities,
1262
+ isFilterCombinationValid
1263
+ ]);
1253
1264
  }
1254
1265
  var createEntityFromDocument = (docSnap, databaseId) => {
1255
1266
  const values = firestoreToCMSModel(docSnap.data());