@rebasepro/firebase 0.0.1-canary.2 → 0.0.1-canary.eb08332

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +97 -3
  2. package/dist/components/FirebaseLoginView.d.ts +3 -3
  3. package/dist/components/RebaseFirebaseApp.d.ts +2 -1
  4. package/dist/components/RebaseFirebaseAppProps.d.ts +9 -13
  5. package/dist/components/social_icons.d.ts +6 -6
  6. package/dist/hooks/index.d.ts +3 -1
  7. package/dist/hooks/useAppCheck.d.ts +2 -2
  8. package/dist/hooks/useBuildUserManagement.d.ts +78 -0
  9. package/dist/hooks/useFirebaseAccessGate.d.ts +44 -0
  10. package/dist/hooks/useFirebaseAuthController.d.ts +2 -2
  11. package/dist/hooks/useFirebaseRealTimeDBDelegate.d.ts +2 -2
  12. package/dist/hooks/useFirebaseStorageSource.d.ts +1 -1
  13. package/dist/hooks/{useFirestoreDataSource.d.ts → useFirestoreDriver.d.ts} +9 -9
  14. package/dist/index.es.js +2598 -2663
  15. package/dist/index.es.js.map +1 -1
  16. package/dist/index.umd.js +2704 -2702
  17. package/dist/index.umd.js.map +1 -1
  18. package/dist/types/auth.d.ts +2 -2
  19. package/dist/types/text_search.d.ts +3 -3
  20. package/dist/utils/algolia.d.ts +7 -6
  21. package/dist/utils/collections_firestore.d.ts +4 -4
  22. package/package.json +75 -65
  23. package/src/components/FirebaseLoginView.tsx +53 -50
  24. package/src/components/RebaseFirebaseApp.tsx +82 -56
  25. package/src/components/RebaseFirebaseAppProps.tsx +10 -24
  26. package/src/components/social_icons.tsx +8 -8
  27. package/src/hooks/index.ts +3 -1
  28. package/src/hooks/useAppCheck.ts +5 -5
  29. package/src/hooks/useBuildUserManagement.tsx +366 -0
  30. package/src/hooks/useFirebaseAccessGate.tsx +145 -0
  31. package/src/hooks/useFirebaseAuthController.ts +6 -8
  32. package/src/hooks/useFirebaseRealTimeDBDelegate.ts +70 -79
  33. package/src/hooks/useFirebaseStorageSource.ts +23 -25
  34. package/src/hooks/useFirestoreDriver.ts +812 -0
  35. package/src/hooks/useInitialiseFirebase.ts +2 -2
  36. package/src/types/auth.tsx +2 -2
  37. package/src/types/text_search.ts +3 -3
  38. package/src/utils/algolia.ts +6 -9
  39. package/src/utils/collections_firestore.ts +25 -29
  40. package/src/utils/database.ts +2 -2
  41. package/src/utils/local_text_search_controller.ts +12 -12
  42. package/src/utils/pinecone.ts +3 -3
  43. package/src/utils/rebase_search_controller.ts +23 -22
  44. package/src/utils/text_search_controller.ts +2 -2
  45. package/dist/social_icons.d.ts +0 -6
  46. package/src/hooks/useFirestoreDataSource.ts +0 -796
  47. package/src/social_icons.tsx +0 -135
@@ -15,18 +15,9 @@ import {
15
15
  startAt
16
16
  } from "@firebase/database";
17
17
  import { useCallback } from "react";
18
- import {
19
- DataSource,
20
- DeleteEntityProps,
21
- Entity,
22
- FetchCollectionProps,
23
- FetchEntityProps,
24
- ListenCollectionProps,
25
- ListenEntityProps,
26
- SaveEntityProps
27
- } from "@rebasepro/core";
28
-
29
- export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: FirebaseApp }): DataSource {
18
+ import { DataDriver, DeleteProps, FetchCollectionProps, FetchOneProps, FilterValues, ListenCollectionProps, ListenOneProps, SaveProps } from "@rebasepro/types";
19
+
20
+ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: FirebaseApp }): DataDriver {
30
21
 
31
22
  const fetchCollection = useCallback(async <M extends Record<string, any>>({
32
23
  path,
@@ -36,7 +27,7 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
36
27
  orderBy,
37
28
  order,
38
29
  searchString
39
- }: FetchCollectionProps<M>): Promise<Entity<M>[]> => {
30
+ }: FetchCollectionProps<M>): Promise<Record<string, unknown>[]> => {
40
31
  if (!firebaseApp) {
41
32
  throw new Error("Firebase app not provided");
42
33
  }
@@ -46,18 +37,17 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
46
37
 
47
38
  // Example to apply "limit" and "startAfter"
48
39
  if (startAfter !== undefined) {
49
- dbQuery = query(dbQuery, orderByKey(), startAt(startAfter as any));
40
+ dbQuery = query(dbQuery, orderByKey(), startAt(String(startAfter)));
50
41
  }
51
42
  if (limit !== undefined) {
52
43
  dbQuery = query(dbQuery, limitToFirst(limit));
53
44
  }
54
45
 
55
- const snapshot = await get(dbQuery);
56
- if (snapshot.exists()) {
57
- return Object.entries(snapshot.val()).map(([id, values]) => ({
58
- id,
59
- path: path,
60
- values: delegateToCMSModel(values) as M,
46
+ const entity = await get(dbQuery);
47
+ if (entity.exists()) {
48
+ return Object.entries(entity.val()).map(([id, values]) => ({
49
+ ...(delegateToCMSModel(values) as Record<string, unknown>),
50
+ id
61
51
  }));
62
52
  }
63
53
  return [];
@@ -65,7 +55,7 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
65
55
 
66
56
  const listenCollection = useCallback(<M extends Record<string, any>>({
67
57
  path,
68
- onUpdate,
58
+ onUpdate
69
59
  // Realtime Database does not directly support onError in onValue
70
60
  }: ListenCollectionProps<M>): () => void => {
71
61
  if (!firebaseApp) {
@@ -74,12 +64,11 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
74
64
  const database = getDatabase(firebaseApp);
75
65
 
76
66
  const dbRef = ref(database, path);
77
- const unsubscribe = onValue(dbRef, (snapshot) => {
78
- if (snapshot.exists()) {
79
- const result: Entity<M>[] = Object.entries(snapshot.val()).map(([id, values]) => ({
80
- id,
81
- path: path,
82
- values: delegateToCMSModel(values) as M,
67
+ const unsubscribe = onValue(dbRef, (entity) => {
68
+ if (entity.exists()) {
69
+ const result: Record<string, unknown>[] = Object.entries(entity.val()).map(([id, values]) => ({
70
+ ...(delegateToCMSModel(values) as Record<string, unknown>),
71
+ id
83
72
  }));
84
73
  onUpdate(result);
85
74
  } else {
@@ -90,44 +79,42 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
90
79
  return () => unsubscribe();
91
80
  }, [firebaseApp]);
92
81
 
93
- const fetchEntity = useCallback(async <M extends Record<string, any>>({
82
+ const fetchOne = useCallback(async <M extends Record<string, any>>({
94
83
  path,
95
- entityId,
96
- }: FetchEntityProps<M>): Promise<Entity<M> | undefined> => {
84
+ id
85
+ }: FetchOneProps<M>): Promise<Record<string, unknown> | undefined> => {
97
86
  if (!firebaseApp) {
98
87
  throw new Error("Firebase app not provided");
99
88
  }
100
89
  const database = getDatabase(firebaseApp);
101
90
 
102
- const snapshot = await get(ref(database, `${path}/${entityId}`));
103
- if (snapshot.exists()) {
91
+ const entity = await get(ref(database, `${path}/${id}`));
92
+ if (entity.exists()) {
104
93
  return {
105
- id: entityId,
106
- path: path,
107
- values: delegateToCMSModel(snapshot.val()) as M
94
+ ...(delegateToCMSModel(entity.val()) as Record<string, unknown>),
95
+ id: id
108
96
  };
109
97
  }
110
98
  return undefined;
111
99
  }, [firebaseApp]);
112
100
 
113
- const listenEntity = useCallback(<M extends Record<string, any>>({
101
+ const listenOne = useCallback(<M extends Record<string, any>>({
114
102
  path,
115
- entityId,
103
+ id,
116
104
  onUpdate,
117
105
  onError
118
- }: ListenEntityProps<M>): () => void => {
106
+ }: ListenOneProps<M>): () => void => {
119
107
  if (!firebaseApp) {
120
108
  throw new Error("Firebase app not provided");
121
109
  }
122
110
  const database = getDatabase(firebaseApp);
123
111
 
124
- const dbRef = ref(database, `${path}/${entityId}`);
125
- const unsubscribe = onValue(dbRef, (snapshot) => {
126
- if (snapshot.exists()) {
112
+ const dbRef = ref(database, `${path}/${id}`);
113
+ const unsubscribe = onValue(dbRef, (entity) => {
114
+ if (entity.exists()) {
127
115
  onUpdate({
128
- id: entityId,
129
- path,
130
- values: delegateToCMSModel(snapshot.val()) as M
116
+ ...(delegateToCMSModel(entity.val()) as Record<string, unknown>),
117
+ id: id
131
118
  });
132
119
  } else {
133
120
  onError?.(new Error("Entity does not exist"));
@@ -137,18 +124,18 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
137
124
  return () => unsubscribe();
138
125
  }, [firebaseApp]);
139
126
 
140
- const saveEntity = useCallback(async <M extends Record<string, any>>({
127
+ const save = useCallback(async <M extends Record<string, any>>({
141
128
  path,
142
- entityId,
143
- values,
144
- }: SaveEntityProps<M>): Promise<Entity<M>> => {
129
+ id,
130
+ values
131
+ }: SaveProps<M>): Promise<Record<string, unknown>> => {
145
132
  if (!firebaseApp) {
146
133
  throw new Error("Firebase app not provided");
147
134
  }
148
135
  const database = getDatabase(firebaseApp);
149
136
 
150
- // If entityId is not provided, a new entity will be created
151
- const finalId = entityId ?? push(ref(database, path)).key;
137
+ // If id is not provided, a new entity will be created
138
+ const finalId = id ?? push(ref(database, path)).key;
152
139
  if (!finalId) {
153
140
  throw new Error("Could not generate a new id");
154
141
  }
@@ -158,41 +145,40 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
158
145
  await set(ref(database, `${path}/${finalId}`), transformedValues);
159
146
 
160
147
  return {
161
- id: finalId,
162
- path: path,
163
- values: values as M
148
+ ...values,
149
+ id: finalId
164
150
  };
165
151
  }, [firebaseApp]);
166
152
 
167
- const deleteEntity = useCallback(async <M extends Record<string, any>>({
168
- entity,
169
- }: DeleteEntityProps<M>): Promise<void> => {
153
+ const deleteOne = useCallback(async <M extends Record<string, any>>({
154
+ row
155
+ }: DeleteProps<M>): Promise<void> => {
170
156
  if (!firebaseApp) {
171
157
  throw new Error("Firebase app not provided");
172
158
  }
173
159
  const database = getDatabase(firebaseApp);
174
160
 
175
- await remove(ref(database, `${entity.path}/${entity.id}`));
161
+ await remove(ref(database, `${row.path}/${row.id}`));
176
162
  }, [firebaseApp]);
177
163
 
178
- // Implementing additional methods required by DataSource
179
- const checkUniqueField = useCallback(async (slug: string, name: string, value: any, entityId?: string | number): Promise<boolean> => {
164
+ // Implementing additional methods required by DataDriver
165
+ const checkUniqueField = useCallback(async (slug: string, name: string, value: unknown, id?: string | number): Promise<boolean> => {
180
166
  if (!firebaseApp) {
181
167
  throw new Error("Firebase app not provided");
182
168
  }
183
169
  const database = getDatabase(firebaseApp);
184
170
 
185
171
  // Simplified example; the Realtime Database does not support querying with "not equal" conditions
186
- const dbRef = query(ref(database, slug), orderByChild(name), startAt(value), limitToFirst(1));
187
- const snapshot = await get(dbRef);
172
+ const dbRef = query(ref(database, slug), orderByChild(name), startAt(value as string | number | boolean | null), limitToFirst(1));
173
+ const entity = await get(dbRef);
188
174
 
189
- if (!snapshot.exists()) {
175
+ if (!entity.exists()) {
190
176
  return true;
191
177
  }
192
178
 
193
179
  // Check if the found entity is the same as the one being checked
194
- const [key, entityValue] = Object.entries(snapshot.val())[0];
195
- if (entityValue && typeof entityValue === "object" && (entityValue as any)[name] === value && key === entityId) {
180
+ const [key, entityValue] = Object.entries(entity.val())[0];
181
+ if (entityValue && typeof entityValue === "object" && (entityValue as Record<string, unknown>)[name] === value && key === id) {
196
182
  return true;
197
183
  }
198
184
 
@@ -203,7 +189,11 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
203
189
  path,
204
190
  filter,
205
191
  sortBy
206
- }: any): boolean => {
192
+ }: {
193
+ path: string;
194
+ filter?: FilterValues<string>;
195
+ sortBy?: [string, "asc" | "desc"];
196
+ }): boolean => {
207
197
  return false;
208
198
  }, []);
209
199
 
@@ -211,10 +201,10 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
211
201
  key: "firebase_rtdb",
212
202
  fetchCollection,
213
203
  listenCollection,
214
- fetchEntity,
215
- listenEntity,
216
- saveEntity,
217
- deleteEntity,
204
+ fetchOne,
205
+ listenOne,
206
+ save,
207
+ delete: deleteOne,
218
208
  checkUniqueField,
219
209
  isFilterCombinationValid,
220
210
  currentTime: () => new Date()
@@ -222,12 +212,11 @@ export function useFirebaseRTDBDelegate({ firebaseApp }: { firebaseApp?: Firebas
222
212
  }
223
213
 
224
214
 
225
-
226
215
  /**
227
216
  * Transform data from RTDB format back to CMS format
228
217
  * This is used internally when fetching/listening to data
229
218
  */
230
- function delegateToCMSModel(data: any): any {
219
+ function delegateToCMSModel(data: unknown): unknown {
231
220
  if (data === null || data === undefined) return null;
232
221
 
233
222
  if (Array.isArray(data)) {
@@ -235,9 +224,9 @@ function delegateToCMSModel(data: any): any {
235
224
  }
236
225
 
237
226
  if (typeof data === "object") {
238
- const result: Record<string, any> = {};
239
- for (const key of Object.keys(data)) {
240
- const childValue = delegateToCMSModel(data[key]);
227
+ const result: Record<string, unknown> = {};
228
+ for (const key of Object.keys(data as Record<string, unknown>)) {
229
+ const childValue = delegateToCMSModel((data as Record<string, unknown>)[key]);
241
230
  if (childValue !== undefined)
242
231
  result[key] = childValue;
243
232
  }
@@ -251,20 +240,21 @@ function delegateToCMSModel(data: any): any {
251
240
  * Transform data from CMS format to RTDB format
252
241
  * This is used internally when saving data
253
242
  */
254
- function cmsToRTDBModel(data: any, database: Database): any {
243
+ function cmsToRTDBModel(data: unknown, database: Database): unknown {
255
244
  if (data === undefined) {
256
245
  return null;
257
246
  } else if (data === null) {
258
247
  return null;
259
248
  } else if (Array.isArray(data)) {
260
249
  return data.filter(v => v !== undefined).map(v => cmsToRTDBModel(v, database));
261
- } else if (data.isEntityReference && data.isEntityReference()) {
262
- return ref(database, `${data.slug}/${data.id}`);
250
+ } else if (typeof data === "object" && data !== null && "isEntityReference" in data && typeof (data as Record<string, unknown>).isEntityReference === "function" && (data as { isEntityReference: () => boolean }).isEntityReference()) {
251
+ const entityRef = data as unknown as { slug: string; id: string };
252
+ return ref(database, `${entityRef.slug}/${entityRef.id}`);
263
253
  } else if (data instanceof Date) {
264
254
  // For dates, convert to ISO string or timestamp.
265
255
  return data.toISOString();
266
256
  } else if (data && typeof data === "object") {
267
- return Object.entries(data)
257
+ return Object.entries(data as Record<string, unknown>)
268
258
  .map(([key, v]) => {
269
259
  const rtdbModel = cmsToRTDBModel(v, database);
270
260
  if (rtdbModel !== undefined)
@@ -272,7 +262,8 @@ function cmsToRTDBModel(data: any, database: Database): any {
272
262
  else
273
263
  return {};
274
264
  })
275
- .reduce((a, b) => ({ ...a, ...b }), {});
265
+ .reduce((a, b) => ({ ...a,
266
+ ...b }), {});
276
267
  }
277
268
  return data;
278
269
  }
@@ -8,7 +8,7 @@ import {
8
8
  ref,
9
9
  uploadBytesResumable
10
10
  } from "@firebase/storage";
11
- import { DownloadConfig, DownloadMetadata, StorageListResult, StorageSource, UploadFileProps } from "@rebasepro/core";
11
+ import { DownloadConfig, DownloadMetadata, StorageListResult, StorageSource, UploadFileProps } from "@rebasepro/types";
12
12
 
13
13
  /**
14
14
  * @group Firebase
@@ -29,10 +29,9 @@ export function useFirebaseStorageSource({
29
29
  const projectId = firebaseApp?.options?.projectId;
30
30
  const urlsCache: Record<string, DownloadConfig> = {};
31
31
  return {
32
- uploadFile({
32
+ putObject({
33
33
  file,
34
- fileName,
35
- path,
34
+ key,
36
35
  metadata,
37
36
  bucket
38
37
  }: UploadFileProps)
@@ -42,9 +41,8 @@ export function useFirebaseStorageSource({
42
41
  const storageBucketUrl = bucket ?? bucketUrl;
43
42
  const storage = getStorage(firebaseApp, storageBucketUrl);
44
43
  if (!storage) throw Error("useFirebaseStorageSource Firebase not initialised");
45
- const usedFilename = fileName ?? file.name;
46
44
 
47
- const storageRef = ref(storage, `${path}/${usedFilename}`);
45
+ const storageRef = ref(storage, key);
48
46
  const uploadTask = uploadBytesResumable(storageRef, file, metadata);
49
47
 
50
48
  return new Promise((resolve, reject) => {
@@ -71,8 +69,8 @@ export function useFirebaseStorageSource({
71
69
  setProgressTimeout();
72
70
 
73
71
  uploadTask.on("state_changed",
74
- (snapshot) => {
75
- const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
72
+ (entity) => {
73
+ const progress = (entity.bytesTransferred / entity.totalBytes) * 100;
76
74
 
77
75
  if (progress > lastProgress) {
78
76
  lastProgress = progress;
@@ -102,8 +100,7 @@ export function useFirebaseStorageSource({
102
100
  } else if (errorMessage.toLowerCase().includes("network")) {
103
101
  reject(new Error("Network error: Check your internet connection"));
104
102
  } else {
105
- const newError = new Error(errorMessage);
106
- (newError as any).code = error?.code;
103
+ const newError = Object.assign(new Error(errorMessage), { code: error?.code });
107
104
  reject(newError);
108
105
  }
109
106
  },
@@ -112,9 +109,9 @@ export function useFirebaseStorageSource({
112
109
  const fullPath = uploadTask.snapshot.ref.fullPath;
113
110
  const bucketName = uploadTask.snapshot.ref.bucket;
114
111
  resolve({
115
- path: fullPath,
112
+ key: fullPath,
116
113
  bucket: bucketName,
117
- storageUrl: `gs://${bucketName}/${fullPath}`
114
+ storageUrl: `s3://${bucketName}/${fullPath}`
118
115
  });
119
116
  }
120
117
  );
@@ -124,7 +121,7 @@ export function useFirebaseStorageSource({
124
121
  }
125
122
  },
126
123
 
127
- async getFile(path: string, bucket?: string): Promise<File | null> {
124
+ async getObject(path: string, bucket?: string): Promise<File | null> {
128
125
  try {
129
126
  if (!firebaseApp) throw Error("useFirebaseStorageSource Firebase not initialised");
130
127
  const storageBucketUrl = bucket ?? bucketUrl;
@@ -135,21 +132,22 @@ export function useFirebaseStorageSource({
135
132
  const response = await fetch(url);
136
133
  const blob = await response.blob();
137
134
  return new File([blob], path);
138
- } catch (e: any) {
139
- 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;
140
137
  throw e;
141
138
  }
142
139
  },
143
140
 
144
- async getDownloadURL(storagePathOrUrl: string, bucket?: string): Promise<DownloadConfig> {
141
+ async getSignedUrl(storagePathOrUrl: string, bucket?: string): Promise<DownloadConfig> {
145
142
  if (!firebaseApp) throw Error("useFirebaseStorageSource Firebase not initialised");
146
143
 
147
- // Support fully-qualified gs:// URLs
144
+ // Support fully-qualified s3:// and gs:// URLs for backward compatibility
148
145
  let resolvedPathOrUrl = storagePathOrUrl;
149
146
  let resolvedBucket = bucket;
150
- if (storagePathOrUrl.startsWith("gs://")) {
151
- // Format: gs://<bucket>/<path>
152
- const withoutProtocol = storagePathOrUrl.substring("gs://".length);
147
+ const match = storagePathOrUrl.match(/^(s3|gs):\/\//);
148
+ if (match) {
149
+ const protocolLength = match[0].length;
150
+ const withoutProtocol = storagePathOrUrl.substring(protocolLength);
153
151
  const firstSlash = withoutProtocol.indexOf("/");
154
152
  if (firstSlash > 0) {
155
153
  resolvedBucket = withoutProtocol.substring(0, firstSlash);
@@ -172,8 +170,8 @@ export function useFirebaseStorageSource({
172
170
  }
173
171
  urlsCache[storagePathOrUrl] = result;
174
172
  return result;
175
- } catch (e: any) {
176
- 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 {
177
175
  url: null,
178
176
  fileNotFound: true
179
177
  };
@@ -181,7 +179,7 @@ export function useFirebaseStorageSource({
181
179
  }
182
180
  },
183
181
 
184
- async list(path: string, options?: {
182
+ async listObjects(prefix: string, options?: {
185
183
  bucket?: string,
186
184
  maxResults?: number,
187
185
  pageToken?: string
@@ -190,14 +188,14 @@ export function useFirebaseStorageSource({
190
188
  const storageBucketUrl = options?.bucket ?? bucketUrl;
191
189
  const storage = getStorage(firebaseApp, storageBucketUrl);
192
190
  if (!storage) throw Error("useFirebaseStorageSource Firebase not initialised");
193
- const folderRef = ref(storage, path);
191
+ const folderRef = ref(storage, prefix);
194
192
  return await list(folderRef, {
195
193
  maxResults: options?.maxResults,
196
194
  pageToken: options?.pageToken
197
195
  });
198
196
  },
199
197
 
200
- async deleteFile(path: string, bucket?: string): Promise<void> {
198
+ async deleteObject(path: string, bucket?: string): Promise<void> {
201
199
  if (!firebaseApp) throw Error("useFirebaseStorageSource Firebase not initialised");
202
200
  const storageBucketUrl = bucket ?? bucketUrl;
203
201
  const storage = getStorage(firebaseApp, storageBucketUrl);