@reause/firebase 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hairyf
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ import { UseAuthOptions, UseAuthReturn, useAuth } from "./useAuth.js";
2
+ import { FirebaseDocRef, UseFirestoreOptions, useFirestore } from "./useFirestore.js";
3
+ import { UseRTDBOptions, UseRTDBReturn, useRTDB } from "./useRTDB.js";
4
+ export { FirebaseDocRef, UseAuthOptions, UseAuthReturn, UseFirestoreOptions, UseRTDBOptions, UseRTDBReturn, useAuth, useFirestore, useRTDB };
@@ -0,0 +1,267 @@
1
+ (function(exports, react, _reause_shared, firebase_database) {
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ //#region useAuth/index.tsx
4
+ /**
5
+ * React port of VueUse's `useAuth`.
6
+ *
7
+ * Map from @vueuse/firebase/useAuth
8
+ * (`source/vueuse/packages/firebase/useAuth/`). Reactive
9
+ * [Firebase Auth](https://firebase.google.com/docs/auth) binding — it exposes
10
+ * the current `user` and an `isAuthenticated` flag, so a component can react to
11
+ * sign-in, sign-out and ID-token refresh events.
12
+ *
13
+ * Adjustment for React:
14
+ * - the `Auth` instance stays the first argument (`useAuth(auth)`), but the
15
+ * return is a plain object `{ isAuthenticated, user, loading, error }` — the
16
+ * values are read during render instead of being Vue refs (`ComputedRef`
17
+ * / `Ref`), and the object is returned rather than a destructured tuple;
18
+ * - `loading` and `error` are additions to upstream. Firebase reports the
19
+ * current auth state **asynchronously**, so a port without `loading` would
20
+ * render the signed-out UI for one frame before the listener corrects it;
21
+ * `loading` starts `true` and flips to `false` with the first callback (a
22
+ * signed-out callback included). `error` stays `null` unless subscribing
23
+ * itself throws;
24
+ * - `user` is seeded from `auth.currentUser` (upstream's
25
+ * `ref(auth.currentUser)`), so an already signed-in visitor renders
26
+ * authenticated on the very first pass, while `loading` is still `true`;
27
+ * - upstream subscribes in `setup()` and **never** unsubscribes; this port
28
+ * subscribes in an effect keyed on `auth` and returns the listener's
29
+ * `Unsubscribe` as cleanup, so a new `Auth` instance re-subscribes (and
30
+ * re-seeds `user`/`loading`/`error`) and unmounting stops the listener — a
31
+ * deliberate React-idiomatic deviation that fixes upstream's leak;
32
+ * - errors on sign-in/sign-out are **not** routed here: the `error` and
33
+ * `completed` callbacks of `onIdTokenChanged` are deprecated in Firebase and
34
+ * documented as never firing. Catch those on the promise returned by
35
+ * `signInWithPopup` / `signOut` itself; `error` only reports a subscription
36
+ * that could not be established;
37
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
38
+ * does not re-subscribe;
39
+ * - nothing runs while rendering, so server rendering is safe.
40
+ *
41
+ * @see https://vueuse.org/firebase/useAuth/
42
+ *
43
+ * @example
44
+ * const auth = getAuth(app)
45
+ * const { isAuthenticated, user, loading, error } = useAuth(auth)
46
+ * if (loading) return <div>Loading...</div>
47
+ * if (!isAuthenticated) return <div>Please log in</div>
48
+ * return <div>Welcome, {user.displayName}</div>
49
+ *
50
+ * @__NO_SIDE_EFFECTS__
51
+ */
52
+ function useAuth(auth, options = {}) {
53
+ const { errorHandler = (err) => console.error(err) } = options;
54
+ const [user, setUser] = (0, react.useState)(() => auth.currentUser);
55
+ const [loading, setLoading] = (0, react.useState)(true);
56
+ const [error, setError] = (0, react.useState)(null);
57
+ const errorHandlerRef = (0, react.useRef)(errorHandler);
58
+ (0, react.useEffect)(() => {
59
+ errorHandlerRef.current = errorHandler;
60
+ }, [errorHandler]);
61
+ const subscribedAuthRef = (0, react.useRef)(auth);
62
+ (0, react.useEffect)(() => {
63
+ if (subscribedAuthRef.current !== auth) {
64
+ subscribedAuthRef.current = auth;
65
+ setUser(auth.currentUser);
66
+ setLoading(true);
67
+ setError(null);
68
+ }
69
+ let unsubscribe;
70
+ let active = true;
71
+ try {
72
+ unsubscribe = auth.onIdTokenChanged((authUser) => {
73
+ if (!active) return;
74
+ setUser(authUser);
75
+ setLoading(false);
76
+ });
77
+ } catch (err) {
78
+ const subscriptionError = err instanceof Error ? err : new Error(String(err));
79
+ setError(subscriptionError);
80
+ setLoading(false);
81
+ errorHandlerRef.current(subscriptionError);
82
+ }
83
+ return () => {
84
+ active = false;
85
+ unsubscribe === null || unsubscribe === void 0 || unsubscribe();
86
+ };
87
+ }, [auth]);
88
+ return {
89
+ isAuthenticated: user !== null,
90
+ user,
91
+ loading,
92
+ error
93
+ };
94
+ }
95
+ //#endregion
96
+ //#region useFirestore/index.tsx
97
+ /**
98
+ * Attach the document `id` as a non-writable property of the snapshot data —
99
+ * ported verbatim from upstream. `data()` may be `undefined` for a deleted
100
+ * document.
101
+ */
102
+ function getData(docRef) {
103
+ const data = docRef.data();
104
+ if (data) Object.defineProperty(data, "id", {
105
+ value: docRef.id.toString(),
106
+ writable: false
107
+ });
108
+ return data;
109
+ }
110
+ /**
111
+ * Slash-parity check, ported verbatim from upstream: a `DocumentReference`
112
+ * path has an odd number of segments (`users/ada`), a `Query` path an even
113
+ * number (`users` or `users/ada/posts`).
114
+ */
115
+ function isDocumentReference(docRef) {
116
+ var _docRef$path;
117
+ return (((_docRef$path = docRef.path) === null || _docRef$path === void 0 ? void 0 : _docRef$path.match(/\//g)) || []).length % 2 !== 0;
118
+ }
119
+ /**
120
+ * React port of VueUse's `useFirestore`.
121
+ *
122
+ * Map from @vueuse/firebase/useFirestore
123
+ * (`source/vueuse/packages/firebase/useFirestore/`). Reactive
124
+ * [Firestore](https://firebase.google.com/docs/firestore) binding — it keeps
125
+ * local state in sync with a document reference or a query, so a component
126
+ * always renders the freshest remote data.
127
+ *
128
+ * Adjustment for React:
129
+ * - `maybeDocRef` is a plain value (upstream accepts `MaybeRef`): read-only
130
+ * value-source parameters take plain `T`. Pass a new reference/query
131
+ * identity to re-subscribe — **keep it stable across renders** (memoize
132
+ * `doc`/`collection`/`query` results): a fresh identity on every render
133
+ * re-subscribes on every render;
134
+ * - the return is the plain state VALUE (not a tuple, not an object) —
135
+ * upstream exposes no setter (0 writable values), so the shape mirrors the
136
+ * read side of upstream's `Ref<T | null>` / `Ref<T[]>`; a document resolves
137
+ * to `T | null` (a deleted document becomes `null`), a query to `T[]`;
138
+ * - the subscription lives in an effect keyed on `maybeDocRef`, so a new
139
+ * ref/query identity re-subscribes and closes the previous `onSnapshot`
140
+ * (upstream's immediate watch); a falsy docRef resets `data` to
141
+ * `initialValue`;
142
+ * - `firebase/firestore` is loaded through a guarded **dynamic** import, so
143
+ * this module never throws at import time when `firebase` is missing — a
144
+ * missing module or a failed `onSnapshot` call surfaces through
145
+ * `errorHandler` instead, and `data` stays at `initialValue`;
146
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
147
+ * does not re-subscribe;
148
+ * - nothing runs while rendering, so server rendering is safe.
149
+ *
150
+ * @see https://vueuse.org/firebase/useFirestore/
151
+ *
152
+ * @example
153
+ * const todos = useFirestore(collection(db, 'todos'))
154
+ * const user = useFirestore(doc(db, 'users', 'my-user-id'))
155
+ *
156
+ * @__NO_SIDE_EFFECTS__
157
+ */
158
+ function useFirestore(maybeDocRef, initialValue = void 0, options = {}) {
159
+ const { errorHandler = (err) => console.error(err), autoDispose = true } = options;
160
+ const [data, setData] = (0, react.useState)(initialValue);
161
+ const initialValueRef = (0, react.useRef)(initialValue);
162
+ const autoDisposeRef = (0, react.useRef)(autoDispose);
163
+ const errorHandlerRef = (0, react.useRef)(errorHandler);
164
+ (0, react.useEffect)(() => {
165
+ errorHandlerRef.current = errorHandler;
166
+ }, [errorHandler]);
167
+ const closeRef = (0, react.useRef)(() => {});
168
+ (0, react.useEffect)(() => {
169
+ closeRef.current();
170
+ closeRef.current = () => {};
171
+ if (!maybeDocRef) {
172
+ setData(initialValueRef.current);
173
+ return;
174
+ }
175
+ let active = true;
176
+ let close;
177
+ import("firebase/firestore").then(({ onSnapshot }) => {
178
+ if (!active) return;
179
+ try {
180
+ var _close;
181
+ if (isDocumentReference(maybeDocRef)) close = onSnapshot(maybeDocRef, (snapshot) => {
182
+ if (!active) return;
183
+ setData(getData(snapshot) || null);
184
+ }, (err) => errorHandlerRef.current(err));
185
+ else close = onSnapshot(maybeDocRef, (snapshot) => {
186
+ if (!active) return;
187
+ setData(snapshot.docs.map(getData).filter(_reause_shared.isDef));
188
+ }, (err) => errorHandlerRef.current(err));
189
+ closeRef.current = (_close = close) !== null && _close !== void 0 ? _close : (() => {});
190
+ } catch (err) {
191
+ errorHandlerRef.current(err instanceof Error ? err : new Error(String(err)));
192
+ }
193
+ }).catch((err) => {
194
+ if (!active) return;
195
+ errorHandlerRef.current(err instanceof Error ? err : new Error(String(err)));
196
+ });
197
+ return () => {
198
+ active = false;
199
+ };
200
+ }, [maybeDocRef]);
201
+ (0, react.useEffect)(() => {
202
+ return () => {
203
+ if (autoDisposeRef.current === true) closeRef.current();
204
+ else if (typeof autoDisposeRef.current === "number") setTimeout(() => {
205
+ closeRef.current();
206
+ }, autoDisposeRef.current);
207
+ };
208
+ }, []);
209
+ return data;
210
+ }
211
+ //#endregion
212
+ //#region useRTDB/index.tsx
213
+ /**
214
+ * React port of VueUse's `useRTDB`.
215
+ *
216
+ * Map from @vueuse/firebase `useRTDB`
217
+ *
218
+ * Reactive [Firebase Realtime Database](https://firebase.google.com/docs/database)
219
+ * binding — keeps local state in sync with a database reference. The listener
220
+ * is registered with `onValue` in a mount effect and feeds `data` with
221
+ * `snapshot.val()` on every database change.
222
+ *
223
+ * Adjustment for React:
224
+ * - upstream returns a writable `Ref<T | undefined>`, so this port returns the
225
+ * `[data, setData]` tuple; `data` starts `undefined` and holds the latest
226
+ * snapshot value;
227
+ * - `setData` writes **local state only** — it does not write to the Realtime
228
+ * Database (upstream's ref is equally local). Use the `firebase/database`
229
+ * write APIs (`set` / `update` / `push`) to persist;
230
+ * - the subscription lives in a `useEffect` keyed on `docRef` and `autoDispose`,
231
+ * so a new `docRef` identity re-subscribes and unsubscribes the previous
232
+ * listener (upstream subscribes once per `setup()` — a deliberate
233
+ * React-idiomatic deviation);
234
+ * - cleanup calls the `onValue` unsubscribe only when `autoDispose` is `true`
235
+ * (upstream parity). `autoDispose: false` means the subscription outlives the
236
+ * component: the caller gets no `off` handle and must live with the leak —
237
+ * discouraged, kept only for upstream parity;
238
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
239
+ * does not re-subscribe.
240
+ *
241
+ * @see https://vueuse.org/useRTDB
242
+ *
243
+ * @example
244
+ * const [todos, setTodos] = useRTDB<Record<string, Todo>>(ref(getDatabase(app), 'todos'))
245
+ *
246
+ * @__NO_SIDE_EFFECTS__
247
+ */
248
+ function useRTDB(docRef, options = {}) {
249
+ const { errorHandler = (err) => console.error(err), autoDispose = true } = options;
250
+ const [data, setData] = (0, react.useState)(void 0);
251
+ const errorHandlerRef = (0, react.useRef)(errorHandler);
252
+ (0, react.useEffect)(() => {
253
+ errorHandlerRef.current = errorHandler;
254
+ }, [errorHandler]);
255
+ (0, react.useEffect)(() => {
256
+ const off = (0, firebase_database.onValue)(docRef, (snapshot) => setData(snapshot.val()), (err) => errorHandlerRef.current(err));
257
+ return () => {
258
+ if (autoDispose) off();
259
+ };
260
+ }, [docRef, autoDispose]);
261
+ return [data, setData];
262
+ }
263
+ //#endregion
264
+ exports.useAuth = useAuth;
265
+ exports.useFirestore = useFirestore;
266
+ exports.useRTDB = useRTDB;
267
+ })(this.reause = this.reause || {}, React, reause, firebase);
@@ -0,0 +1 @@
1
+ (function(e,t,n,r){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function i(e,n={}){let{errorHandler:r=e=>console.error(e)}=n,[i,a]=(0,t.useState)(()=>e.currentUser),[o,s]=(0,t.useState)(!0),[c,l]=(0,t.useState)(null),u=(0,t.useRef)(r);(0,t.useEffect)(()=>{u.current=r},[r]);let d=(0,t.useRef)(e);return(0,t.useEffect)(()=>{d.current!==e&&(d.current=e,a(e.currentUser),s(!0),l(null));let t,n=!0;try{t=e.onIdTokenChanged(e=>{n&&(a(e),s(!1))})}catch(e){let t=e instanceof Error?e:Error(String(e));l(t),s(!1),u.current(t)}return()=>{n=!1,t==null||t()}},[e]),{isAuthenticated:i!==null,user:i,loading:o,error:c}}function a(e){let t=e.data();return t&&Object.defineProperty(t,"id",{value:e.id.toString(),writable:!1}),t}function o(e){var t;return(((t=e.path)==null?void 0:t.match(/\//g))||[]).length%2!=0}function s(e,r=void 0,i={}){let{errorHandler:s=e=>console.error(e),autoDispose:c=!0}=i,[l,u]=(0,t.useState)(r),d=(0,t.useRef)(r),f=(0,t.useRef)(c),p=(0,t.useRef)(s);(0,t.useEffect)(()=>{p.current=s},[s]);let m=(0,t.useRef)(()=>{});return(0,t.useEffect)(()=>{if(m.current(),m.current=()=>{},!e){u(d.current);return}let t=!0,r;return import(`firebase/firestore`).then(({onSnapshot:i})=>{if(t)try{var s;r=o(e)?i(e,e=>{t&&u(a(e)||null)},e=>p.current(e)):i(e,e=>{t&&u(e.docs.map(a).filter(n.isDef))},e=>p.current(e)),m.current=(s=r)==null?(()=>{}):s}catch(e){p.current(e instanceof Error?e:Error(String(e)))}}).catch(e=>{t&&p.current(e instanceof Error?e:Error(String(e)))}),()=>{t=!1}},[e]),(0,t.useEffect)(()=>()=>{f.current===!0?m.current():typeof f.current==`number`&&setTimeout(()=>{m.current()},f.current)},[]),l}function c(e,n={}){let{errorHandler:i=e=>console.error(e),autoDispose:a=!0}=n,[o,s]=(0,t.useState)(void 0),c=(0,t.useRef)(i);return(0,t.useEffect)(()=>{c.current=i},[i]),(0,t.useEffect)(()=>{let t=(0,r.onValue)(e,e=>s(e.val()),e=>c.current(e));return()=>{a&&t()}},[e,a]),[o,s]}e.useAuth=i,e.useFirestore=s,e.useRTDB=c})(this.reause=this.reause||{},React,reause,firebase);
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { useAuth } from "./useAuth.js";
2
+ import { useFirestore } from "./useFirestore.js";
3
+ import { useRTDB } from "./useRTDB.js";
4
+ export { useAuth, useFirestore, useRTDB };
@@ -0,0 +1,87 @@
1
+ import { Auth, User } from "firebase/auth";
2
+ //#region useAuth/index.d.ts
3
+ export interface UseAuthOptions {
4
+ /**
5
+ * Custom error handler for auth subscription errors.
6
+ *
7
+ * @default (error) => console.error(error)
8
+ */
9
+ errorHandler?: (err: Error) => void;
10
+ }
11
+ /**
12
+ * Result object of `useAuth` — the plain-value counterpart of upstream's
13
+ * `{ isAuthenticated, user }` refs, plus the `loading` and `error` states.
14
+ */
15
+ export interface UseAuthReturn {
16
+ /**
17
+ * Whether a user is currently authenticated (upstream's `isAuthenticated`
18
+ * computed): `true` whenever `user` is not `null`.
19
+ */
20
+ isAuthenticated: boolean;
21
+ /**
22
+ * The current Firebase user, or `null` if not authenticated (upstream's
23
+ * `user` ref). Seeded from `auth.currentUser` and kept in sync by
24
+ * `onIdTokenChanged`.
25
+ */
26
+ user: User | null;
27
+ /**
28
+ * Whether the auth state is still being resolved: `true` on the first render
29
+ * and until `onIdTokenChanged` reports the current state (or the subscription
30
+ * fails), then `false` — including when the reported state is signed out.
31
+ */
32
+ loading: boolean;
33
+ /**
34
+ * The error thrown while subscribing to `auth.onIdTokenChanged`, or `null`.
35
+ */
36
+ error: Error | null;
37
+ }
38
+ /**
39
+ * React port of VueUse's `useAuth`.
40
+ *
41
+ * Map from @vueuse/firebase/useAuth
42
+ * (`source/vueuse/packages/firebase/useAuth/`). Reactive
43
+ * [Firebase Auth](https://firebase.google.com/docs/auth) binding — it exposes
44
+ * the current `user` and an `isAuthenticated` flag, so a component can react to
45
+ * sign-in, sign-out and ID-token refresh events.
46
+ *
47
+ * Adjustment for React:
48
+ * - the `Auth` instance stays the first argument (`useAuth(auth)`), but the
49
+ * return is a plain object `{ isAuthenticated, user, loading, error }` — the
50
+ * values are read during render instead of being Vue refs (`ComputedRef`
51
+ * / `Ref`), and the object is returned rather than a destructured tuple;
52
+ * - `loading` and `error` are additions to upstream. Firebase reports the
53
+ * current auth state **asynchronously**, so a port without `loading` would
54
+ * render the signed-out UI for one frame before the listener corrects it;
55
+ * `loading` starts `true` and flips to `false` with the first callback (a
56
+ * signed-out callback included). `error` stays `null` unless subscribing
57
+ * itself throws;
58
+ * - `user` is seeded from `auth.currentUser` (upstream's
59
+ * `ref(auth.currentUser)`), so an already signed-in visitor renders
60
+ * authenticated on the very first pass, while `loading` is still `true`;
61
+ * - upstream subscribes in `setup()` and **never** unsubscribes; this port
62
+ * subscribes in an effect keyed on `auth` and returns the listener's
63
+ * `Unsubscribe` as cleanup, so a new `Auth` instance re-subscribes (and
64
+ * re-seeds `user`/`loading`/`error`) and unmounting stops the listener — a
65
+ * deliberate React-idiomatic deviation that fixes upstream's leak;
66
+ * - errors on sign-in/sign-out are **not** routed here: the `error` and
67
+ * `completed` callbacks of `onIdTokenChanged` are deprecated in Firebase and
68
+ * documented as never firing. Catch those on the promise returned by
69
+ * `signInWithPopup` / `signOut` itself; `error` only reports a subscription
70
+ * that could not be established;
71
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
72
+ * does not re-subscribe;
73
+ * - nothing runs while rendering, so server rendering is safe.
74
+ *
75
+ * @see https://vueuse.org/firebase/useAuth/
76
+ *
77
+ * @example
78
+ * const auth = getAuth(app)
79
+ * const { isAuthenticated, user, loading, error } = useAuth(auth)
80
+ * if (loading) return <div>Loading...</div>
81
+ * if (!isAuthenticated) return <div>Please log in</div>
82
+ * return <div>Welcome, {user.displayName}</div>
83
+ *
84
+ * @__NO_SIDE_EFFECTS__
85
+ */
86
+ export declare function useAuth(auth: Auth, options?: UseAuthOptions): UseAuthReturn;
87
+ //#endregion
@@ -0,0 +1,97 @@
1
+ (function(exports, react) {
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ //#region useAuth/index.tsx
4
+ /**
5
+ * React port of VueUse's `useAuth`.
6
+ *
7
+ * Map from @vueuse/firebase/useAuth
8
+ * (`source/vueuse/packages/firebase/useAuth/`). Reactive
9
+ * [Firebase Auth](https://firebase.google.com/docs/auth) binding — it exposes
10
+ * the current `user` and an `isAuthenticated` flag, so a component can react to
11
+ * sign-in, sign-out and ID-token refresh events.
12
+ *
13
+ * Adjustment for React:
14
+ * - the `Auth` instance stays the first argument (`useAuth(auth)`), but the
15
+ * return is a plain object `{ isAuthenticated, user, loading, error }` — the
16
+ * values are read during render instead of being Vue refs (`ComputedRef`
17
+ * / `Ref`), and the object is returned rather than a destructured tuple;
18
+ * - `loading` and `error` are additions to upstream. Firebase reports the
19
+ * current auth state **asynchronously**, so a port without `loading` would
20
+ * render the signed-out UI for one frame before the listener corrects it;
21
+ * `loading` starts `true` and flips to `false` with the first callback (a
22
+ * signed-out callback included). `error` stays `null` unless subscribing
23
+ * itself throws;
24
+ * - `user` is seeded from `auth.currentUser` (upstream's
25
+ * `ref(auth.currentUser)`), so an already signed-in visitor renders
26
+ * authenticated on the very first pass, while `loading` is still `true`;
27
+ * - upstream subscribes in `setup()` and **never** unsubscribes; this port
28
+ * subscribes in an effect keyed on `auth` and returns the listener's
29
+ * `Unsubscribe` as cleanup, so a new `Auth` instance re-subscribes (and
30
+ * re-seeds `user`/`loading`/`error`) and unmounting stops the listener — a
31
+ * deliberate React-idiomatic deviation that fixes upstream's leak;
32
+ * - errors on sign-in/sign-out are **not** routed here: the `error` and
33
+ * `completed` callbacks of `onIdTokenChanged` are deprecated in Firebase and
34
+ * documented as never firing. Catch those on the promise returned by
35
+ * `signInWithPopup` / `signOut` itself; `error` only reports a subscription
36
+ * that could not be established;
37
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
38
+ * does not re-subscribe;
39
+ * - nothing runs while rendering, so server rendering is safe.
40
+ *
41
+ * @see https://vueuse.org/firebase/useAuth/
42
+ *
43
+ * @example
44
+ * const auth = getAuth(app)
45
+ * const { isAuthenticated, user, loading, error } = useAuth(auth)
46
+ * if (loading) return <div>Loading...</div>
47
+ * if (!isAuthenticated) return <div>Please log in</div>
48
+ * return <div>Welcome, {user.displayName}</div>
49
+ *
50
+ * @__NO_SIDE_EFFECTS__
51
+ */
52
+ function useAuth(auth, options = {}) {
53
+ const { errorHandler = (err) => console.error(err) } = options;
54
+ const [user, setUser] = (0, react.useState)(() => auth.currentUser);
55
+ const [loading, setLoading] = (0, react.useState)(true);
56
+ const [error, setError] = (0, react.useState)(null);
57
+ const errorHandlerRef = (0, react.useRef)(errorHandler);
58
+ (0, react.useEffect)(() => {
59
+ errorHandlerRef.current = errorHandler;
60
+ }, [errorHandler]);
61
+ const subscribedAuthRef = (0, react.useRef)(auth);
62
+ (0, react.useEffect)(() => {
63
+ if (subscribedAuthRef.current !== auth) {
64
+ subscribedAuthRef.current = auth;
65
+ setUser(auth.currentUser);
66
+ setLoading(true);
67
+ setError(null);
68
+ }
69
+ let unsubscribe;
70
+ let active = true;
71
+ try {
72
+ unsubscribe = auth.onIdTokenChanged((authUser) => {
73
+ if (!active) return;
74
+ setUser(authUser);
75
+ setLoading(false);
76
+ });
77
+ } catch (err) {
78
+ const subscriptionError = err instanceof Error ? err : new Error(String(err));
79
+ setError(subscriptionError);
80
+ setLoading(false);
81
+ errorHandlerRef.current(subscriptionError);
82
+ }
83
+ return () => {
84
+ active = false;
85
+ unsubscribe === null || unsubscribe === void 0 || unsubscribe();
86
+ };
87
+ }, [auth]);
88
+ return {
89
+ isAuthenticated: user !== null,
90
+ user,
91
+ loading,
92
+ error
93
+ };
94
+ }
95
+ //#endregion
96
+ exports.useAuth = useAuth;
97
+ })(this.reause = this.reause || {}, React);
@@ -0,0 +1 @@
1
+ (function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function n(e,n={}){let{errorHandler:r=e=>console.error(e)}=n,[i,a]=(0,t.useState)(()=>e.currentUser),[o,s]=(0,t.useState)(!0),[c,l]=(0,t.useState)(null),u=(0,t.useRef)(r);(0,t.useEffect)(()=>{u.current=r},[r]);let d=(0,t.useRef)(e);return(0,t.useEffect)(()=>{d.current!==e&&(d.current=e,a(e.currentUser),s(!0),l(null));let t,n=!0;try{t=e.onIdTokenChanged(e=>{n&&(a(e),s(!1))})}catch(e){let t=e instanceof Error?e:Error(String(e));l(t),s(!1),u.current(t)}return()=>{n=!1,t==null||t()}},[e]),{isAuthenticated:i!==null,user:i,loading:o,error:c}}e.useAuth=n})(this.reause=this.reause||{},React);
@@ -0,0 +1,95 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ //#region useAuth/index.tsx
3
+ /**
4
+ * React port of VueUse's `useAuth`.
5
+ *
6
+ * Map from @vueuse/firebase/useAuth
7
+ * (`source/vueuse/packages/firebase/useAuth/`). Reactive
8
+ * [Firebase Auth](https://firebase.google.com/docs/auth) binding — it exposes
9
+ * the current `user` and an `isAuthenticated` flag, so a component can react to
10
+ * sign-in, sign-out and ID-token refresh events.
11
+ *
12
+ * Adjustment for React:
13
+ * - the `Auth` instance stays the first argument (`useAuth(auth)`), but the
14
+ * return is a plain object `{ isAuthenticated, user, loading, error }` — the
15
+ * values are read during render instead of being Vue refs (`ComputedRef`
16
+ * / `Ref`), and the object is returned rather than a destructured tuple;
17
+ * - `loading` and `error` are additions to upstream. Firebase reports the
18
+ * current auth state **asynchronously**, so a port without `loading` would
19
+ * render the signed-out UI for one frame before the listener corrects it;
20
+ * `loading` starts `true` and flips to `false` with the first callback (a
21
+ * signed-out callback included). `error` stays `null` unless subscribing
22
+ * itself throws;
23
+ * - `user` is seeded from `auth.currentUser` (upstream's
24
+ * `ref(auth.currentUser)`), so an already signed-in visitor renders
25
+ * authenticated on the very first pass, while `loading` is still `true`;
26
+ * - upstream subscribes in `setup()` and **never** unsubscribes; this port
27
+ * subscribes in an effect keyed on `auth` and returns the listener's
28
+ * `Unsubscribe` as cleanup, so a new `Auth` instance re-subscribes (and
29
+ * re-seeds `user`/`loading`/`error`) and unmounting stops the listener — a
30
+ * deliberate React-idiomatic deviation that fixes upstream's leak;
31
+ * - errors on sign-in/sign-out are **not** routed here: the `error` and
32
+ * `completed` callbacks of `onIdTokenChanged` are deprecated in Firebase and
33
+ * documented as never firing. Catch those on the promise returned by
34
+ * `signInWithPopup` / `signOut` itself; `error` only reports a subscription
35
+ * that could not be established;
36
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
37
+ * does not re-subscribe;
38
+ * - nothing runs while rendering, so server rendering is safe.
39
+ *
40
+ * @see https://vueuse.org/firebase/useAuth/
41
+ *
42
+ * @example
43
+ * const auth = getAuth(app)
44
+ * const { isAuthenticated, user, loading, error } = useAuth(auth)
45
+ * if (loading) return <div>Loading...</div>
46
+ * if (!isAuthenticated) return <div>Please log in</div>
47
+ * return <div>Welcome, {user.displayName}</div>
48
+ *
49
+ * @__NO_SIDE_EFFECTS__
50
+ */
51
+ function useAuth(auth, options = {}) {
52
+ const { errorHandler = (err) => console.error(err) } = options;
53
+ const [user, setUser] = useState(() => auth.currentUser);
54
+ const [loading, setLoading] = useState(true);
55
+ const [error, setError] = useState(null);
56
+ const errorHandlerRef = useRef(errorHandler);
57
+ useEffect(() => {
58
+ errorHandlerRef.current = errorHandler;
59
+ }, [errorHandler]);
60
+ const subscribedAuthRef = useRef(auth);
61
+ useEffect(() => {
62
+ if (subscribedAuthRef.current !== auth) {
63
+ subscribedAuthRef.current = auth;
64
+ setUser(auth.currentUser);
65
+ setLoading(true);
66
+ setError(null);
67
+ }
68
+ let unsubscribe;
69
+ let active = true;
70
+ try {
71
+ unsubscribe = auth.onIdTokenChanged((authUser) => {
72
+ if (!active) return;
73
+ setUser(authUser);
74
+ setLoading(false);
75
+ });
76
+ } catch (err) {
77
+ const subscriptionError = err instanceof Error ? err : new Error(String(err));
78
+ setError(subscriptionError);
79
+ setLoading(false);
80
+ errorHandlerRef.current(subscriptionError);
81
+ }
82
+ return () => {
83
+ active = false;
84
+ unsubscribe === null || unsubscribe === void 0 || unsubscribe();
85
+ };
86
+ }, [auth]);
87
+ return {
88
+ isAuthenticated: user !== null,
89
+ user,
90
+ loading,
91
+ error
92
+ };
93
+ }
94
+ //#endregion
95
+ export { useAuth };
@@ -0,0 +1,25 @@
1
+ import { DocumentData, DocumentReference, Query } from "firebase/firestore";
2
+ //#region useFirestore/index.d.ts
3
+ export interface UseFirestoreOptions {
4
+ /**
5
+ * Custom error handler for Firestore subscription errors.
6
+ *
7
+ * @default (error) => console.error(error)
8
+ */
9
+ errorHandler?: (err: Error) => void;
10
+ /**
11
+ * Automatically unsubscribe when the component unmounts. Pass a number to
12
+ * delay the unsubscribe by that many milliseconds (upstream's
13
+ * `useTimeoutFn`-based delayed dispose).
14
+ *
15
+ * @default true
16
+ */
17
+ autoDispose?: boolean | number;
18
+ }
19
+ export type FirebaseDocRef<T> = Query<T> | DocumentReference<T>;
20
+ type Falsy = false | 0 | '' | null | undefined;
21
+ export declare function useFirestore<T extends DocumentData>(maybeDocRef: DocumentReference<T> | Falsy, initialValue: T, options?: UseFirestoreOptions): T | null;
22
+ export declare function useFirestore<T extends DocumentData>(maybeDocRef: Query<T> | Falsy, initialValue: T[], options?: UseFirestoreOptions): T[];
23
+ export declare function useFirestore<T extends DocumentData>(maybeDocRef: DocumentReference<T> | Falsy, initialValue?: T | undefined | null, options?: UseFirestoreOptions): T | undefined | null;
24
+ export declare function useFirestore<T extends DocumentData>(maybeDocRef: Query<T> | Falsy, initialValue?: T[], options?: UseFirestoreOptions): T[] | undefined;
25
+ //#endregion
@@ -0,0 +1,120 @@
1
+ (function(exports, _reause_shared, react) {
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ //#region useFirestore/index.tsx
4
+ /**
5
+ * Attach the document `id` as a non-writable property of the snapshot data —
6
+ * ported verbatim from upstream. `data()` may be `undefined` for a deleted
7
+ * document.
8
+ */
9
+ function getData(docRef) {
10
+ const data = docRef.data();
11
+ if (data) Object.defineProperty(data, "id", {
12
+ value: docRef.id.toString(),
13
+ writable: false
14
+ });
15
+ return data;
16
+ }
17
+ /**
18
+ * Slash-parity check, ported verbatim from upstream: a `DocumentReference`
19
+ * path has an odd number of segments (`users/ada`), a `Query` path an even
20
+ * number (`users` or `users/ada/posts`).
21
+ */
22
+ function isDocumentReference(docRef) {
23
+ var _docRef$path;
24
+ return (((_docRef$path = docRef.path) === null || _docRef$path === void 0 ? void 0 : _docRef$path.match(/\//g)) || []).length % 2 !== 0;
25
+ }
26
+ /**
27
+ * React port of VueUse's `useFirestore`.
28
+ *
29
+ * Map from @vueuse/firebase/useFirestore
30
+ * (`source/vueuse/packages/firebase/useFirestore/`). Reactive
31
+ * [Firestore](https://firebase.google.com/docs/firestore) binding — it keeps
32
+ * local state in sync with a document reference or a query, so a component
33
+ * always renders the freshest remote data.
34
+ *
35
+ * Adjustment for React:
36
+ * - `maybeDocRef` is a plain value (upstream accepts `MaybeRef`): read-only
37
+ * value-source parameters take plain `T`. Pass a new reference/query
38
+ * identity to re-subscribe — **keep it stable across renders** (memoize
39
+ * `doc`/`collection`/`query` results): a fresh identity on every render
40
+ * re-subscribes on every render;
41
+ * - the return is the plain state VALUE (not a tuple, not an object) —
42
+ * upstream exposes no setter (0 writable values), so the shape mirrors the
43
+ * read side of upstream's `Ref<T | null>` / `Ref<T[]>`; a document resolves
44
+ * to `T | null` (a deleted document becomes `null`), a query to `T[]`;
45
+ * - the subscription lives in an effect keyed on `maybeDocRef`, so a new
46
+ * ref/query identity re-subscribes and closes the previous `onSnapshot`
47
+ * (upstream's immediate watch); a falsy docRef resets `data` to
48
+ * `initialValue`;
49
+ * - `firebase/firestore` is loaded through a guarded **dynamic** import, so
50
+ * this module never throws at import time when `firebase` is missing — a
51
+ * missing module or a failed `onSnapshot` call surfaces through
52
+ * `errorHandler` instead, and `data` stays at `initialValue`;
53
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
54
+ * does not re-subscribe;
55
+ * - nothing runs while rendering, so server rendering is safe.
56
+ *
57
+ * @see https://vueuse.org/firebase/useFirestore/
58
+ *
59
+ * @example
60
+ * const todos = useFirestore(collection(db, 'todos'))
61
+ * const user = useFirestore(doc(db, 'users', 'my-user-id'))
62
+ *
63
+ * @__NO_SIDE_EFFECTS__
64
+ */
65
+ function useFirestore(maybeDocRef, initialValue = void 0, options = {}) {
66
+ const { errorHandler = (err) => console.error(err), autoDispose = true } = options;
67
+ const [data, setData] = (0, react.useState)(initialValue);
68
+ const initialValueRef = (0, react.useRef)(initialValue);
69
+ const autoDisposeRef = (0, react.useRef)(autoDispose);
70
+ const errorHandlerRef = (0, react.useRef)(errorHandler);
71
+ (0, react.useEffect)(() => {
72
+ errorHandlerRef.current = errorHandler;
73
+ }, [errorHandler]);
74
+ const closeRef = (0, react.useRef)(() => {});
75
+ (0, react.useEffect)(() => {
76
+ closeRef.current();
77
+ closeRef.current = () => {};
78
+ if (!maybeDocRef) {
79
+ setData(initialValueRef.current);
80
+ return;
81
+ }
82
+ let active = true;
83
+ let close;
84
+ import("firebase/firestore").then(({ onSnapshot }) => {
85
+ if (!active) return;
86
+ try {
87
+ var _close;
88
+ if (isDocumentReference(maybeDocRef)) close = onSnapshot(maybeDocRef, (snapshot) => {
89
+ if (!active) return;
90
+ setData(getData(snapshot) || null);
91
+ }, (err) => errorHandlerRef.current(err));
92
+ else close = onSnapshot(maybeDocRef, (snapshot) => {
93
+ if (!active) return;
94
+ setData(snapshot.docs.map(getData).filter(_reause_shared.isDef));
95
+ }, (err) => errorHandlerRef.current(err));
96
+ closeRef.current = (_close = close) !== null && _close !== void 0 ? _close : (() => {});
97
+ } catch (err) {
98
+ errorHandlerRef.current(err instanceof Error ? err : new Error(String(err)));
99
+ }
100
+ }).catch((err) => {
101
+ if (!active) return;
102
+ errorHandlerRef.current(err instanceof Error ? err : new Error(String(err)));
103
+ });
104
+ return () => {
105
+ active = false;
106
+ };
107
+ }, [maybeDocRef]);
108
+ (0, react.useEffect)(() => {
109
+ return () => {
110
+ if (autoDisposeRef.current === true) closeRef.current();
111
+ else if (typeof autoDisposeRef.current === "number") setTimeout(() => {
112
+ closeRef.current();
113
+ }, autoDisposeRef.current);
114
+ };
115
+ }, []);
116
+ return data;
117
+ }
118
+ //#endregion
119
+ exports.useFirestore = useFirestore;
120
+ })(this.reause = this.reause || {}, reause, React);
@@ -0,0 +1 @@
1
+ (function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function r(e){let t=e.data();return t&&Object.defineProperty(t,"id",{value:e.id.toString(),writable:!1}),t}function i(e){var t;return(((t=e.path)==null?void 0:t.match(/\//g))||[]).length%2!=0}function a(e,a=void 0,o={}){let{errorHandler:s=e=>console.error(e),autoDispose:c=!0}=o,[l,u]=(0,n.useState)(a),d=(0,n.useRef)(a),f=(0,n.useRef)(c),p=(0,n.useRef)(s);(0,n.useEffect)(()=>{p.current=s},[s]);let m=(0,n.useRef)(()=>{});return(0,n.useEffect)(()=>{if(m.current(),m.current=()=>{},!e){u(d.current);return}let n=!0,a;return import(`firebase/firestore`).then(({onSnapshot:o})=>{if(n)try{var s;a=i(e)?o(e,e=>{n&&u(r(e)||null)},e=>p.current(e)):o(e,e=>{n&&u(e.docs.map(r).filter(t.isDef))},e=>p.current(e)),m.current=(s=a)==null?(()=>{}):s}catch(e){p.current(e instanceof Error?e:Error(String(e)))}}).catch(e=>{n&&p.current(e instanceof Error?e:Error(String(e)))}),()=>{n=!1}},[e]),(0,n.useEffect)(()=>()=>{f.current===!0?m.current():typeof f.current==`number`&&setTimeout(()=>{m.current()},f.current)},[]),l}e.useFirestore=a})(this.reause=this.reause||{},reause,React);
@@ -0,0 +1,119 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { isDef } from "@reause/shared";
3
+ //#region useFirestore/index.tsx
4
+ /**
5
+ * Attach the document `id` as a non-writable property of the snapshot data —
6
+ * ported verbatim from upstream. `data()` may be `undefined` for a deleted
7
+ * document.
8
+ */
9
+ function getData(docRef) {
10
+ const data = docRef.data();
11
+ if (data) Object.defineProperty(data, "id", {
12
+ value: docRef.id.toString(),
13
+ writable: false
14
+ });
15
+ return data;
16
+ }
17
+ /**
18
+ * Slash-parity check, ported verbatim from upstream: a `DocumentReference`
19
+ * path has an odd number of segments (`users/ada`), a `Query` path an even
20
+ * number (`users` or `users/ada/posts`).
21
+ */
22
+ function isDocumentReference(docRef) {
23
+ var _docRef$path;
24
+ return (((_docRef$path = docRef.path) === null || _docRef$path === void 0 ? void 0 : _docRef$path.match(/\//g)) || []).length % 2 !== 0;
25
+ }
26
+ /**
27
+ * React port of VueUse's `useFirestore`.
28
+ *
29
+ * Map from @vueuse/firebase/useFirestore
30
+ * (`source/vueuse/packages/firebase/useFirestore/`). Reactive
31
+ * [Firestore](https://firebase.google.com/docs/firestore) binding — it keeps
32
+ * local state in sync with a document reference or a query, so a component
33
+ * always renders the freshest remote data.
34
+ *
35
+ * Adjustment for React:
36
+ * - `maybeDocRef` is a plain value (upstream accepts `MaybeRef`): read-only
37
+ * value-source parameters take plain `T`. Pass a new reference/query
38
+ * identity to re-subscribe — **keep it stable across renders** (memoize
39
+ * `doc`/`collection`/`query` results): a fresh identity on every render
40
+ * re-subscribes on every render;
41
+ * - the return is the plain state VALUE (not a tuple, not an object) —
42
+ * upstream exposes no setter (0 writable values), so the shape mirrors the
43
+ * read side of upstream's `Ref<T | null>` / `Ref<T[]>`; a document resolves
44
+ * to `T | null` (a deleted document becomes `null`), a query to `T[]`;
45
+ * - the subscription lives in an effect keyed on `maybeDocRef`, so a new
46
+ * ref/query identity re-subscribes and closes the previous `onSnapshot`
47
+ * (upstream's immediate watch); a falsy docRef resets `data` to
48
+ * `initialValue`;
49
+ * - `firebase/firestore` is loaded through a guarded **dynamic** import, so
50
+ * this module never throws at import time when `firebase` is missing — a
51
+ * missing module or a failed `onSnapshot` call surfaces through
52
+ * `errorHandler` instead, and `data` stays at `initialValue`;
53
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
54
+ * does not re-subscribe;
55
+ * - nothing runs while rendering, so server rendering is safe.
56
+ *
57
+ * @see https://vueuse.org/firebase/useFirestore/
58
+ *
59
+ * @example
60
+ * const todos = useFirestore(collection(db, 'todos'))
61
+ * const user = useFirestore(doc(db, 'users', 'my-user-id'))
62
+ *
63
+ * @__NO_SIDE_EFFECTS__
64
+ */
65
+ function useFirestore(maybeDocRef, initialValue = void 0, options = {}) {
66
+ const { errorHandler = (err) => console.error(err), autoDispose = true } = options;
67
+ const [data, setData] = useState(initialValue);
68
+ const initialValueRef = useRef(initialValue);
69
+ const autoDisposeRef = useRef(autoDispose);
70
+ const errorHandlerRef = useRef(errorHandler);
71
+ useEffect(() => {
72
+ errorHandlerRef.current = errorHandler;
73
+ }, [errorHandler]);
74
+ const closeRef = useRef(() => {});
75
+ useEffect(() => {
76
+ closeRef.current();
77
+ closeRef.current = () => {};
78
+ if (!maybeDocRef) {
79
+ setData(initialValueRef.current);
80
+ return;
81
+ }
82
+ let active = true;
83
+ let close;
84
+ import("firebase/firestore").then(({ onSnapshot }) => {
85
+ if (!active) return;
86
+ try {
87
+ var _close;
88
+ if (isDocumentReference(maybeDocRef)) close = onSnapshot(maybeDocRef, (snapshot) => {
89
+ if (!active) return;
90
+ setData(getData(snapshot) || null);
91
+ }, (err) => errorHandlerRef.current(err));
92
+ else close = onSnapshot(maybeDocRef, (snapshot) => {
93
+ if (!active) return;
94
+ setData(snapshot.docs.map(getData).filter(isDef));
95
+ }, (err) => errorHandlerRef.current(err));
96
+ closeRef.current = (_close = close) !== null && _close !== void 0 ? _close : (() => {});
97
+ } catch (err) {
98
+ errorHandlerRef.current(err instanceof Error ? err : new Error(String(err)));
99
+ }
100
+ }).catch((err) => {
101
+ if (!active) return;
102
+ errorHandlerRef.current(err instanceof Error ? err : new Error(String(err)));
103
+ });
104
+ return () => {
105
+ active = false;
106
+ };
107
+ }, [maybeDocRef]);
108
+ useEffect(() => {
109
+ return () => {
110
+ if (autoDisposeRef.current === true) closeRef.current();
111
+ else if (typeof autoDisposeRef.current === "number") setTimeout(() => {
112
+ closeRef.current();
113
+ }, autoDisposeRef.current);
114
+ };
115
+ }, []);
116
+ return data;
117
+ }
118
+ //#endregion
119
+ export { useFirestore };
@@ -0,0 +1,59 @@
1
+ import { DatabaseReference } from "firebase/database";
2
+ //#region useRTDB/index.d.ts
3
+ export interface UseRTDBOptions {
4
+ /**
5
+ * Custom error handler for database errors.
6
+ *
7
+ * @default (error) => console.error(error)
8
+ */
9
+ errorHandler?: (err: Error) => void;
10
+ /**
11
+ * Automatically unsubscribe from the database reference when the component
12
+ * unmounts.
13
+ *
14
+ * @default true
15
+ */
16
+ autoDispose?: boolean;
17
+ }
18
+ /**
19
+ * Result tuple of `useRTDB`, mirroring upstream's writable Vue ref:
20
+ * `[data, setData]`.
21
+ */
22
+ export type UseRTDBReturn<T> = [data: T | undefined, setData: (value: T | undefined) => void];
23
+ /**
24
+ * React port of VueUse's `useRTDB`.
25
+ *
26
+ * Map from @vueuse/firebase `useRTDB`
27
+ *
28
+ * Reactive [Firebase Realtime Database](https://firebase.google.com/docs/database)
29
+ * binding — keeps local state in sync with a database reference. The listener
30
+ * is registered with `onValue` in a mount effect and feeds `data` with
31
+ * `snapshot.val()` on every database change.
32
+ *
33
+ * Adjustment for React:
34
+ * - upstream returns a writable `Ref<T | undefined>`, so this port returns the
35
+ * `[data, setData]` tuple; `data` starts `undefined` and holds the latest
36
+ * snapshot value;
37
+ * - `setData` writes **local state only** — it does not write to the Realtime
38
+ * Database (upstream's ref is equally local). Use the `firebase/database`
39
+ * write APIs (`set` / `update` / `push`) to persist;
40
+ * - the subscription lives in a `useEffect` keyed on `docRef` and `autoDispose`,
41
+ * so a new `docRef` identity re-subscribes and unsubscribes the previous
42
+ * listener (upstream subscribes once per `setup()` — a deliberate
43
+ * React-idiomatic deviation);
44
+ * - cleanup calls the `onValue` unsubscribe only when `autoDispose` is `true`
45
+ * (upstream parity). `autoDispose: false` means the subscription outlives the
46
+ * component: the caller gets no `off` handle and must live with the leak —
47
+ * discouraged, kept only for upstream parity;
48
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
49
+ * does not re-subscribe.
50
+ *
51
+ * @see https://vueuse.org/useRTDB
52
+ *
53
+ * @example
54
+ * const [todos, setTodos] = useRTDB<Record<string, Todo>>(ref(getDatabase(app), 'todos'))
55
+ *
56
+ * @__NO_SIDE_EFFECTS__
57
+ */
58
+ export declare function useRTDB<T = any>(docRef: DatabaseReference, options?: UseRTDBOptions): UseRTDBReturn<T>;
59
+ //#endregion
@@ -0,0 +1,56 @@
1
+ (function(exports, firebase_database, react) {
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ //#region useRTDB/index.tsx
4
+ /**
5
+ * React port of VueUse's `useRTDB`.
6
+ *
7
+ * Map from @vueuse/firebase `useRTDB`
8
+ *
9
+ * Reactive [Firebase Realtime Database](https://firebase.google.com/docs/database)
10
+ * binding — keeps local state in sync with a database reference. The listener
11
+ * is registered with `onValue` in a mount effect and feeds `data` with
12
+ * `snapshot.val()` on every database change.
13
+ *
14
+ * Adjustment for React:
15
+ * - upstream returns a writable `Ref<T | undefined>`, so this port returns the
16
+ * `[data, setData]` tuple; `data` starts `undefined` and holds the latest
17
+ * snapshot value;
18
+ * - `setData` writes **local state only** — it does not write to the Realtime
19
+ * Database (upstream's ref is equally local). Use the `firebase/database`
20
+ * write APIs (`set` / `update` / `push`) to persist;
21
+ * - the subscription lives in a `useEffect` keyed on `docRef` and `autoDispose`,
22
+ * so a new `docRef` identity re-subscribes and unsubscribes the previous
23
+ * listener (upstream subscribes once per `setup()` — a deliberate
24
+ * React-idiomatic deviation);
25
+ * - cleanup calls the `onValue` unsubscribe only when `autoDispose` is `true`
26
+ * (upstream parity). `autoDispose: false` means the subscription outlives the
27
+ * component: the caller gets no `off` handle and must live with the leak —
28
+ * discouraged, kept only for upstream parity;
29
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
30
+ * does not re-subscribe.
31
+ *
32
+ * @see https://vueuse.org/useRTDB
33
+ *
34
+ * @example
35
+ * const [todos, setTodos] = useRTDB<Record<string, Todo>>(ref(getDatabase(app), 'todos'))
36
+ *
37
+ * @__NO_SIDE_EFFECTS__
38
+ */
39
+ function useRTDB(docRef, options = {}) {
40
+ const { errorHandler = (err) => console.error(err), autoDispose = true } = options;
41
+ const [data, setData] = (0, react.useState)(void 0);
42
+ const errorHandlerRef = (0, react.useRef)(errorHandler);
43
+ (0, react.useEffect)(() => {
44
+ errorHandlerRef.current = errorHandler;
45
+ }, [errorHandler]);
46
+ (0, react.useEffect)(() => {
47
+ const off = (0, firebase_database.onValue)(docRef, (snapshot) => setData(snapshot.val()), (err) => errorHandlerRef.current(err));
48
+ return () => {
49
+ if (autoDispose) off();
50
+ };
51
+ }, [docRef, autoDispose]);
52
+ return [data, setData];
53
+ }
54
+ //#endregion
55
+ exports.useRTDB = useRTDB;
56
+ })(this.reause = this.reause || {}, firebase, React);
@@ -0,0 +1 @@
1
+ (function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function r(e,r={}){let{errorHandler:i=e=>console.error(e),autoDispose:a=!0}=r,[o,s]=(0,n.useState)(void 0),c=(0,n.useRef)(i);return(0,n.useEffect)(()=>{c.current=i},[i]),(0,n.useEffect)(()=>{let n=(0,t.onValue)(e,e=>s(e.val()),e=>c.current(e));return()=>{a&&n()}},[e,a]),[o,s]}e.useRTDB=r})(this.reause=this.reause||{},firebase,React);
@@ -0,0 +1,55 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { onValue } from "firebase/database";
3
+ //#region useRTDB/index.tsx
4
+ /**
5
+ * React port of VueUse's `useRTDB`.
6
+ *
7
+ * Map from @vueuse/firebase `useRTDB`
8
+ *
9
+ * Reactive [Firebase Realtime Database](https://firebase.google.com/docs/database)
10
+ * binding — keeps local state in sync with a database reference. The listener
11
+ * is registered with `onValue` in a mount effect and feeds `data` with
12
+ * `snapshot.val()` on every database change.
13
+ *
14
+ * Adjustment for React:
15
+ * - upstream returns a writable `Ref<T | undefined>`, so this port returns the
16
+ * `[data, setData]` tuple; `data` starts `undefined` and holds the latest
17
+ * snapshot value;
18
+ * - `setData` writes **local state only** — it does not write to the Realtime
19
+ * Database (upstream's ref is equally local). Use the `firebase/database`
20
+ * write APIs (`set` / `update` / `push`) to persist;
21
+ * - the subscription lives in a `useEffect` keyed on `docRef` and `autoDispose`,
22
+ * so a new `docRef` identity re-subscribes and unsubscribes the previous
23
+ * listener (upstream subscribes once per `setup()` — a deliberate
24
+ * React-idiomatic deviation);
25
+ * - cleanup calls the `onValue` unsubscribe only when `autoDispose` is `true`
26
+ * (upstream parity). `autoDispose: false` means the subscription outlives the
27
+ * component: the caller gets no `off` handle and must live with the leak —
28
+ * discouraged, kept only for upstream parity;
29
+ * - the latest `errorHandler` is read from a ref, so passing an inline handler
30
+ * does not re-subscribe.
31
+ *
32
+ * @see https://vueuse.org/useRTDB
33
+ *
34
+ * @example
35
+ * const [todos, setTodos] = useRTDB<Record<string, Todo>>(ref(getDatabase(app), 'todos'))
36
+ *
37
+ * @__NO_SIDE_EFFECTS__
38
+ */
39
+ function useRTDB(docRef, options = {}) {
40
+ const { errorHandler = (err) => console.error(err), autoDispose = true } = options;
41
+ const [data, setData] = useState(void 0);
42
+ const errorHandlerRef = useRef(errorHandler);
43
+ useEffect(() => {
44
+ errorHandlerRef.current = errorHandler;
45
+ }, [errorHandler]);
46
+ useEffect(() => {
47
+ const off = onValue(docRef, (snapshot) => setData(snapshot.val()), (err) => errorHandlerRef.current(err));
48
+ return () => {
49
+ if (autoDispose) off();
50
+ };
51
+ }, [docRef, autoDispose]);
52
+ return [data, setData];
53
+ }
54
+ //#endregion
55
+ export { useRTDB };
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@reause/firebase",
3
+ "type": "module",
4
+ "version": "0.1.2",
5
+ "description": "Realtime bindings for Firebase — React port of @vueuse/firebase",
6
+ "license": "MIT",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": "./dist/index.js",
10
+ "./*": "./dist/*",
11
+ "./useAuth": "./dist/useAuth.js",
12
+ "./useFirestore": "./dist/useFirestore.js",
13
+ "./useRTDB": "./dist/useRTDB.js",
14
+ "./package.json": "./package.json"
15
+ },
16
+ "main": "./dist/index.js",
17
+ "module": "./dist/index.js",
18
+ "unpkg": "./dist/index.iife.min.js",
19
+ "jsdelivr": "./dist/index.iife.min.js",
20
+ "types": "./dist/index.d.ts",
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "peerDependencies": {
25
+ "firebase": ">=9.0.0",
26
+ "react": ">=18"
27
+ },
28
+ "scripts": {
29
+ "build": "tsdown"
30
+ }
31
+ }