From 89d788b4b41d307f8a829dfa0bcf4f280bf02029 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Fri, 11 Sep 2026 13:44:24 -0500 Subject: [PATCH 01/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20tap=20de?= =?UTF-8?q?precation=20cleanup.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- performance/index.ts | 84 +++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/performance/index.ts b/performance/index.ts index 0e05cd2..69786a1 100644 --- a/performance/index.ts +++ b/performance/index.ts @@ -50,12 +50,10 @@ const trace$ = (traceId: string) => { export const trace = (name: string) => (source$: Observable) => new Observable((subscriber) => { const traceSubscription = trace$(name).subscribe(); return source$.pipe( - tap( - () => traceSubscription.unsubscribe(), - () => { - }, - () => traceSubscription.unsubscribe(), - ), + tap({ + next: () => traceSubscription.unsubscribe(), + complete: () => traceSubscription.unsubscribe(), + }), ).subscribe(subscriber); }); @@ -73,14 +71,16 @@ export const traceUntil = ( options?: { orComplete?: boolean }, ) => (source$: Observable) => new Observable((subscriber) => { const traceSubscription = trace$(name).subscribe(); - return source$.pipe( - tap( - (a) => test(a) && traceSubscription.unsubscribe(), - () => { - }, - () => options && options.orComplete && traceSubscription.unsubscribe(), - ), - ).subscribe(subscriber); + return source$ + .pipe( + tap({ + next: (a) => test(a) && traceSubscription.unsubscribe(), + complete: () => + options && + options.orComplete && + traceSubscription.unsubscribe(), + }), + ).subscribe(subscriber); }); /** @@ -98,23 +98,27 @@ export const traceWhile = ( options?: { orComplete?: boolean }, ) => (source$: Observable) => new Observable((subscriber) => { let traceSubscription: Subscription | undefined; - return source$.pipe( - tap( - (a) => { - if (test(a)) { - traceSubscription = traceSubscription || trace$(name).subscribe(); - } else { - if (traceSubscription) { - traceSubscription.unsubscribe(); - } - traceSubscription = undefined; + return source$ + .pipe( + tap({ + next: (a) => { + if (test(a)) { + traceSubscription = + traceSubscription || trace$(name).subscribe(); + } else { + if (traceSubscription) { + traceSubscription.unsubscribe(); } - }, - () => { - }, - () => options && options.orComplete && traceSubscription && traceSubscription.unsubscribe(), - ), - ).subscribe(subscriber); + traceSubscription = undefined; + } + }, + complete: () => + options && + options.orComplete && + traceSubscription && + traceSubscription.unsubscribe(), + }), + ).subscribe(subscriber); }); /** @@ -126,13 +130,9 @@ export const traceWhile = ( export const traceUntilComplete = (name: string) => (source$: Observable) => new Observable((subscriber) => { const traceSubscription = trace$(name).subscribe(); return source$.pipe( - tap( - () => { - }, - () => { - }, - () => traceSubscription.unsubscribe(), - ), + tap({ + complete: () => traceSubscription.unsubscribe(), + }), ).subscribe(subscriber); }); @@ -145,12 +145,8 @@ export const traceUntilComplete = (name: string) => (source$: Observabl export const traceUntilFirst = (name: string) => (source$: Observable) => new Observable((subscriber) => { const traceSubscription = trace$(name).subscribe(); return source$.pipe( - tap( - () => traceSubscription.unsubscribe(), - () => { - }, - () => { - }, - ), + tap({ + complete: () => traceSubscription.unsubscribe(), + }), ).subscribe(subscriber); }); From 98905a573e49a72cb514213bd6d1f326fc51480f Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Fri, 11 Sep 2026 13:50:43 -0500 Subject: [PATCH 02/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20storage?= =?UTF-8?q?=20return=20type=20for=20getMetadata=20function.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- storage/index.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/storage/index.ts b/storage/index.ts index 9dad2d8..5bb2a78 100644 --- a/storage/index.ts +++ b/storage/index.ts @@ -7,14 +7,23 @@ import { import {Observable, from} from 'rxjs'; import {map, shareReplay} from 'rxjs/operators'; -import type {UploadTaskSnapshot, StorageReference, UploadMetadata, StringFormat, UploadTask, UploadResult} from 'firebase/storage'; +import type { + UploadTaskSnapshot, + StorageReference, + UploadMetadata, + StringFormat, + UploadTask, + UploadResult, + FullMetadata, + StorageError, +} from 'firebase/storage'; export function fromTask(task: UploadTask): Observable { return new Observable((subscriber) => { let lastSnapshot: UploadTaskSnapshot | null = null; let complete = false; let hasError = false; - let error: any = null; + let error: StorageError | null = null; const emit = (snapshot: UploadTaskSnapshot) => { lastSnapshot = snapshot; @@ -74,9 +83,13 @@ export function getDownloadURL(ref: StorageReference): Observable { return from(_getDownloadURL(ref)); } -// TODO: fix storage typing in firebase, then apply the same fix here -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function getMetadata(ref: StorageReference): Observable { +/** + * Retrieves the metadata for a given storage reference. + * + * @param ref The storage reference for which to retrieve metadata. + * @returns An observable that emits the metadata for the given reference. + */ +export function getMetadata(ref: StorageReference): Observable { return from(_getMetadata(ref)); } From 38fd4a0d3f4d8b054ce6843248736a229f19f833 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Fri, 11 Sep 2026 14:04:37 -0500 Subject: [PATCH 03/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20enhance?= =?UTF-8?q?=20type=20definitions=20for=20collectionCount=20and=20CountSnap?= =?UTF-8?q?shot=20functions.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firestore/collection/index.ts | 8 ++++++-- firestore/lite/interfaces.ts | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/firestore/collection/index.ts b/firestore/collection/index.ts index 1a2ae88..810be1e 100644 --- a/firestore/collection/index.ts +++ b/firestore/collection/index.ts @@ -298,10 +298,14 @@ export function collectionData( ); } -export function collectionCountSnap(query: Query): Observable { +export function collectionCountSnap( + query: Query, +): Observable> { return from(getCountFromServer(query)); } -export function collectionCount(query: Query): Observable { +export function collectionCount( + query: Query, +): Observable { return collectionCountSnap(query).pipe(map((snap) => snap.data().count)); } diff --git a/firestore/lite/interfaces.ts b/firestore/lite/interfaces.ts index a9ba75c..a4d6332 100644 --- a/firestore/lite/interfaces.ts +++ b/firestore/lite/interfaces.ts @@ -2,10 +2,20 @@ import type * as lite from 'firebase/firestore/lite'; export type DocumentReference = lite.DocumentReference; export type DocumentData = lite.DocumentData; -export type Query = lite.Query; +export type Query< + AppModelType, + DbModelType extends DocumentData = DocumentData, +> = lite.Query; export type DocumentSnapshot = lite.DocumentSnapshot; export type QuerySnapshot = lite.QuerySnapshot; export type QueryDocumentSnapshot = lite.QueryDocumentSnapshot; -export type CountSnapshot = lite.AggregateQuerySnapshot<{ - count: lite.AggregateField; -}, any, DocumentData>; +export type CountSnapshot< + AppModelType = DocumentData, + DbModelType extends DocumentData = DocumentData, +> = lite.AggregateQuerySnapshot< + { + count: lite.AggregateField; + }, + AppModelType, + DbModelType +>; From d7bdfc8d565620f304dd5a2bbb041ed89e6b06a3 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Fri, 11 Sep 2026 15:10:33 -0500 Subject: [PATCH 04/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20preserve?= =?UTF-8?q?=20converter=20instances=20in=20docData=20when=20idField=20is?= =?UTF-8?q?=20set?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firestore/document/index.ts | 8 ++++---- test/firestore.test.ts | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/firestore/document/index.ts b/firestore/document/index.ts index 0025501..86ffc16 100644 --- a/firestore/document/index.ts +++ b/firestore/document/index.ts @@ -54,8 +54,8 @@ export function snapToData( return data; } - return { - ...data, - [options.idField]: snapshot.id, - }; + // Preserve converter instances and custom prototypes by mutating the original object + // instead of creating a new one with spread syntax. + (data as Record)[options.idField as string] = snapshot.id; + return data; } diff --git a/test/firestore.test.ts b/test/firestore.test.ts index 0274c3a..b80df39 100644 --- a/test/firestore.test.ts +++ b/test/firestore.test.ts @@ -416,6 +416,29 @@ describe('RxFire Firestore', () => { }); }); + it('docData should preserve converter instances when idField is set', (done: jest.DoneCallback) => { + class Folk { + constructor(public name: string) {} + static fromFirestore(snap: QueryDocumentSnapshot) { + return new Folk(snap.data().name); + } + static toFirestore(model: Folk) { + return model; + } + } + + seedTest(firestore).then(({davidDoc}) => { + const unwrapped = docData(davidDoc.withConverter(Folk), {idField: 'UID'}); + + unwrapped.pipe(take(1)).subscribe((val) => { + expect(val).toBeInstanceOf(Folk); + expect((val as Folk).name).toBe('David'); + expect((val as Folk & {UID: string}).UID).toBe('david'); + done(); + }); + }); + }); + /** * TODO(jamesdaniels) * Having trouble gettings these test green with the emulators From 8262258e8044c9f70ff44899f03b3ffeb3df6866 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Fri, 11 Sep 2026 15:11:02 -0500 Subject: [PATCH 05/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20enhance?= =?UTF-8?q?=20type=20definitions=20for=20collection=20count=20functions=20?= =?UTF-8?q?at=20firestore=20lite.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firestore/lite/collection/index.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/firestore/lite/collection/index.ts b/firestore/lite/collection/index.ts index 2bab09c..9dcd1f3 100644 --- a/firestore/lite/collection/index.ts +++ b/firestore/lite/collection/index.ts @@ -48,10 +48,15 @@ export function collectionData( ); } -export function collectionCountSnap(query: Query): Observable { - return from(getCount(query)); +export function collectionCountSnap< + AppModelType = DocumentData, + DbModelType extends DocumentData = DocumentData, +>(query: Query): Observable> { + return from(getCount(query) as Promise>); } -export function collectionCount(query: Query): Observable { +export function collectionCount( + query: Query, +): Observable { return collectionCountSnap(query).pipe(map((snap) => snap.data().count)); } From a85637cad76f417288fdb36e8f2d9f042298d3ea Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 14:15:34 -0500 Subject: [PATCH 06/15] =?UTF-8?q?=F0=9F=9A=A8=20lint:=20clean=20the=20inde?= =?UTF-8?q?ntation=20or=20trailing=20whitespaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firestore/collection/index.ts | 4 +-- performance/index.ts | 66 +++++++++++++++++------------------ storage/index.ts | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/firestore/collection/index.ts b/firestore/collection/index.ts index 810be1e..e19f41f 100644 --- a/firestore/collection/index.ts +++ b/firestore/collection/index.ts @@ -299,13 +299,13 @@ export function collectionData( } export function collectionCountSnap( - query: Query, + query: Query, ): Observable> { return from(getCountFromServer(query)); } export function collectionCount( - query: Query, + query: Query, ): Observable { return collectionCountSnap(query).pipe(map((snap) => snap.data().count)); } diff --git a/performance/index.ts b/performance/index.ts index 69786a1..88069a1 100644 --- a/performance/index.ts +++ b/performance/index.ts @@ -50,10 +50,10 @@ const trace$ = (traceId: string) => { export const trace = (name: string) => (source$: Observable) => new Observable((subscriber) => { const traceSubscription = trace$(name).subscribe(); return source$.pipe( - tap({ - next: () => traceSubscription.unsubscribe(), - complete: () => traceSubscription.unsubscribe(), - }), + tap({ + next: () => traceSubscription.unsubscribe(), + complete: () => traceSubscription.unsubscribe(), + }), ).subscribe(subscriber); }); @@ -72,15 +72,15 @@ export const traceUntil = ( ) => (source$: Observable) => new Observable((subscriber) => { const traceSubscription = trace$(name).subscribe(); return source$ - .pipe( - tap({ - next: (a) => test(a) && traceSubscription.unsubscribe(), - complete: () => - options && + .pipe( + tap({ + next: (a) => test(a) && traceSubscription.unsubscribe(), + complete: () => + options && options.orComplete && traceSubscription.unsubscribe(), - }), - ).subscribe(subscriber); + }), + ).subscribe(subscriber); }); /** @@ -99,26 +99,26 @@ export const traceWhile = ( ) => (source$: Observable) => new Observable((subscriber) => { let traceSubscription: Subscription | undefined; return source$ - .pipe( - tap({ - next: (a) => { - if (test(a)) { - traceSubscription = + .pipe( + tap({ + next: (a) => { + if (test(a)) { + traceSubscription = traceSubscription || trace$(name).subscribe(); - } else { - if (traceSubscription) { - traceSubscription.unsubscribe(); - } - traceSubscription = undefined; - } - }, - complete: () => - options && + } else { + if (traceSubscription) { + traceSubscription.unsubscribe(); + } + traceSubscription = undefined; + } + }, + complete: () => + options && options.orComplete && traceSubscription && traceSubscription.unsubscribe(), - }), - ).subscribe(subscriber); + }), + ).subscribe(subscriber); }); /** @@ -130,9 +130,9 @@ export const traceWhile = ( export const traceUntilComplete = (name: string) => (source$: Observable) => new Observable((subscriber) => { const traceSubscription = trace$(name).subscribe(); return source$.pipe( - tap({ - complete: () => traceSubscription.unsubscribe(), - }), + tap({ + complete: () => traceSubscription.unsubscribe(), + }), ).subscribe(subscriber); }); @@ -145,8 +145,8 @@ export const traceUntilComplete = (name: string) => (source$: Observabl export const traceUntilFirst = (name: string) => (source$: Observable) => new Observable((subscriber) => { const traceSubscription = trace$(name).subscribe(); return source$.pipe( - tap({ - complete: () => traceSubscription.unsubscribe(), - }), + tap({ + complete: () => traceSubscription.unsubscribe(), + }), ).subscribe(subscriber); }); diff --git a/storage/index.ts b/storage/index.ts index 5bb2a78..6b6c085 100644 --- a/storage/index.ts +++ b/storage/index.ts @@ -85,7 +85,7 @@ export function getDownloadURL(ref: StorageReference): Observable { /** * Retrieves the metadata for a given storage reference. - * + * * @param ref The storage reference for which to retrieve metadata. * @returns An observable that emits the metadata for the given reference. */ From 889a165611a35b33b5538cc929eb943d1f0f2ccc Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 14:20:27 -0500 Subject: [PATCH 07/15] =?UTF-8?q?=F0=9F=9A=91=EF=B8=8F=20fix:=20traceUntil?= =?UTF-8?q?First=20to=20unsubscribe=20on=20next=20instead=20of=20complete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- performance/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/performance/index.ts b/performance/index.ts index 88069a1..78239c9 100644 --- a/performance/index.ts +++ b/performance/index.ts @@ -146,7 +146,7 @@ export const traceUntilFirst = (name: string) => (source$: Observable traceSubscription.unsubscribe(), + next: () => traceSubscription.unsubscribe(), }), ).subscribe(subscriber); }); From 4cf1554e1af78e3cfe31c93b5d40cc9bda750127 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 14:56:32 -0500 Subject: [PATCH 08/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20CountSna?= =?UTF-8?q?pshot=20for=20firestore=20interface=20(not=20lite).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firestore/collection/index.ts | 3 +-- firestore/interfaces.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/firestore/collection/index.ts b/firestore/collection/index.ts index e19f41f..ef0a1a2 100644 --- a/firestore/collection/index.ts +++ b/firestore/collection/index.ts @@ -33,9 +33,8 @@ import { pairwise, } from 'rxjs/operators'; import {snapToData} from '../document'; -import {DocumentChangeType, DocumentChange, Query, QueryDocumentSnapshot, QuerySnapshot, DocumentData} from '../interfaces'; +import {DocumentChangeType, DocumentChange, Query, QueryDocumentSnapshot, QuerySnapshot, DocumentData, CountSnapshot} from '../interfaces'; import {SnapshotOptions, getCountFromServer, refEqual} from 'firebase/firestore'; -import {CountSnapshot} from '../lite/interfaces'; const ALL_EVENTS: DocumentChangeType[] = ['added', 'modified', 'removed']; /** diff --git a/firestore/interfaces.ts b/firestore/interfaces.ts index 0341fad..9dfea0a 100644 --- a/firestore/interfaces.ts +++ b/firestore/interfaces.ts @@ -7,3 +7,11 @@ export type QuerySnapshot = import('firebase/firestore').QuerySnapshot; export type DocumentChangeType = import('firebase/firestore').DocumentChangeType; export type DocumentChange = import('firebase/firestore').DocumentChange; export type QueryDocumentSnapshot = import('firebase/firestore').QueryDocumentSnapshot; +export type CountSnapshot< + AppModelType = DocumentData, + DbModelType extends DocumentData = DocumentData, +> = import('firebase/firestore').AggregateQuerySnapshot< + {count: import('firebase/firestore').AggregateField}, + AppModelType, + DbModelType +>; From dccc55da7877c5ae027f4d3219b2ca032a541ea7 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:02:49 -0500 Subject: [PATCH 09/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20update?= =?UTF-8?q?=20snapToData=20to=20use=20Object.assign=20for=20preserving=20c?= =?UTF-8?q?onverter=20instances?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firestore/document/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firestore/document/index.ts b/firestore/document/index.ts index 86ffc16..f42efb0 100644 --- a/firestore/document/index.ts +++ b/firestore/document/index.ts @@ -56,6 +56,6 @@ export function snapToData( // Preserve converter instances and custom prototypes by mutating the original object // instead of creating a new one with spread syntax. - (data as Record)[options.idField as string] = snapshot.id; + Object.assign(data, {[options.idField]: snapshot.id}); return data; } From 1732631411f06a8df07fd1e49ce3ca792d6f2afd Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:03:49 -0500 Subject: [PATCH 10/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20remove?= =?UTF-8?q?=20unnecessary=20type=20assertion=20in=20collectionCountSnap=20?= =?UTF-8?q?lite=20function.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firestore/lite/collection/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firestore/lite/collection/index.ts b/firestore/lite/collection/index.ts index 9dcd1f3..806fb2a 100644 --- a/firestore/lite/collection/index.ts +++ b/firestore/lite/collection/index.ts @@ -52,7 +52,7 @@ export function collectionCountSnap< AppModelType = DocumentData, DbModelType extends DocumentData = DocumentData, >(query: Query): Observable> { - return from(getCount(query) as Promise>); + return from(getCount(query)); } export function collectionCount( From 7fdb7b000d22150b7c6e64404ab80d35b5c83ec9 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:08:52 -0500 Subject: [PATCH 11/15] =?UTF-8?q?=F0=9F=93=9D=20docs(storage):=20update=20?= =?UTF-8?q?getMetadata=20description=20and=20return=20type.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/storage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/storage.md b/docs/storage.md index 55f8d01..3eeff55 100644 --- a/docs/storage.md +++ b/docs/storage.md @@ -90,14 +90,14 @@ getDownloadURL(davidRef) ``` ### `getMetadata()` -The `getMetadata()` function creates an observable that emits the URL of the file's metadta. +The `getMetadata()` function creates an observable that emits the full set of object metadata, including read-only properties.. | | | |-----------------|------------------------------------------| | **function** | `getMetadata()` | | **params** | `import('firebase/storage').StorageReference` | | **import path** | `rxfire/storage` | -| **return** | `Observable` | +| **return** | `Observable` | #### TypeScript Example ```ts From ea37bbabd1be2d5e78cbe73545a97ad60f59b408 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:12:50 -0500 Subject: [PATCH 12/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20minor=20?= =?UTF-8?q?renaming.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- performance/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/performance/index.ts b/performance/index.ts index 78239c9..fa5b34b 100644 --- a/performance/index.ts +++ b/performance/index.ts @@ -74,7 +74,7 @@ export const traceUntil = ( return source$ .pipe( tap({ - next: (a) => test(a) && traceSubscription.unsubscribe(), + next: (value) => test(value) && traceSubscription.unsubscribe(), complete: () => options && options.orComplete && @@ -101,8 +101,8 @@ export const traceWhile = ( return source$ .pipe( tap({ - next: (a) => { - if (test(a)) { + next: (value) => { + if (test(value)) { traceSubscription = traceSubscription || trace$(name).subscribe(); } else { From 0709515c9cfe8adb666678b6ecec266bbbb66a25 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:37:04 -0500 Subject: [PATCH 13/15] =?UTF-8?q?=E2=9C=85=20test(firestore):=20pass=20gen?= =?UTF-8?q?eric=20for=20docData.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/firestore.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/firestore.test.ts b/test/firestore.test.ts index b80df39..b65f18a 100644 --- a/test/firestore.test.ts +++ b/test/firestore.test.ts @@ -428,12 +428,11 @@ describe('RxFire Firestore', () => { } seedTest(firestore).then(({davidDoc}) => { - const unwrapped = docData(davidDoc.withConverter(Folk), {idField: 'UID'}); + const unwrapped = docData(davidDoc.withConverter(Folk), {idField: 'UID'}); unwrapped.pipe(take(1)).subscribe((val) => { expect(val).toBeInstanceOf(Folk); - expect((val as Folk).name).toBe('David'); - expect((val as Folk & {UID: string}).UID).toBe('david'); + expect(val).toEqual(expect.objectContaining({name: 'David', UID: 'david'})); done(); }); }); From 8151258fee3260dad774247e7b6171555ae70b33 Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:52:56 -0500 Subject: [PATCH 14/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20update?= =?UTF-8?q?=20firestore=20lite=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docData and collectionData now uses `keyof` for idField option. --- firestore/lite/collection/index.ts | 4 ++-- firestore/lite/document/index.ts | 24 ++++++++++++------------ test/firestore-lite.test.ts | 22 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/firestore/lite/collection/index.ts b/firestore/lite/collection/index.ts index 806fb2a..f85c79b 100644 --- a/firestore/lite/collection/index.ts +++ b/firestore/lite/collection/index.ts @@ -35,10 +35,10 @@ export function collection(query: Query): Observable( +export function collectionData( query: Query, options: { - idField?: string + idField?: keyof R }={}, ): Observable { return collection(query).pipe( diff --git a/firestore/lite/document/index.ts b/firestore/lite/document/index.ts index 6fd8849..f126ad3 100644 --- a/firestore/lite/document/index.ts +++ b/firestore/lite/document/index.ts @@ -29,30 +29,30 @@ export function doc(ref: DocumentReference): Observable( +export function docData( ref: DocumentReference, options: { - idField?: string + idField?: keyof R, }={}, -): Observable { +): Observable { return doc(ref).pipe(map((snap) => snapToData(snap, options) as T)); } -export function snapToData( +export function snapToData( snapshot: DocumentSnapshot, options: { - idField?: string, + idField?: keyof R, }={}, -): {} | undefined { - // TODO clean up the typings - const data = snapshot.data() as any; +): T | R | undefined { + const data = snapshot.data(); // match the behavior of the JS SDK when the snapshot doesn't exist // it's possible with data converters too that the user didn't return an object - if (!snapshot.exists() || typeof data !== 'object' || data === null) { + if (!snapshot.exists() || typeof data !== 'object' || data === null || !options.idField) { return data; } - if (options.idField) { - data[options.idField] = snapshot.id; - } + + // Preserve converter instances and custom prototypes by mutating the original object + // instead of creating a new one with spread syntax. + Object.assign(data, {[options.idField]: snapshot.id}); return data; } diff --git a/test/firestore-lite.test.ts b/test/firestore-lite.test.ts index 18ff4cb..bd0d561 100644 --- a/test/firestore-lite.test.ts +++ b/test/firestore-lite.test.ts @@ -214,6 +214,28 @@ describe('RxFire firestore/lite', () => { }); }); }); + + it('docData should preserve converter instances when idField is set', (done: jest.DoneCallback) => { + class Folk { + constructor(public name: string) {} + static fromFirestore(snap: QueryDocumentSnapshot) { + return new Folk(snap.data().name); + } + static toFirestore(model: Folk) { + return model; + } + } + + seedTest(firestore).then(({davidDoc}) => { + const unwrapped = docData(davidDoc.withConverter(Folk), {idField: 'UID'}); + + unwrapped.pipe(take(1)).subscribe((val) => { + expect(val).toBeInstanceOf(Folk); + expect(val).toEqual(expect.objectContaining({name: 'David', UID: 'david'})); + done(); + }); + }); + }); }); describe('Aggregations', () => { From 7086a09a334f066070c6d7ca215a5ec1f541abbb Mon Sep 17 00:00:00 2001 From: KingDarBoja <30560560+KingDarBoja@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:04:34 -0500 Subject: [PATCH 15/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20types:=20simplify?= =?UTF-8?q?=20firestore=20Query=20lite=20type.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firestore/lite/collection/index.ts | 11 +++++++---- firestore/lite/interfaces.ts | 5 +---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/firestore/lite/collection/index.ts b/firestore/lite/collection/index.ts index f85c79b..412d1d6 100644 --- a/firestore/lite/collection/index.ts +++ b/firestore/lite/collection/index.ts @@ -50,13 +50,16 @@ export function collectionData( export function collectionCountSnap< AppModelType = DocumentData, - DbModelType extends DocumentData = DocumentData, ->(query: Query): Observable> { + // DbModelType extends DocumentData = DocumentData, +>(query: Query): Observable> { return from(getCount(query)); } -export function collectionCount( - query: Query, +export function collectionCount< + AppModelType = DocumentData, + // DbModelType extends DocumentData = DocumentData +>( + query: Query, ): Observable { return collectionCountSnap(query).pipe(map((snap) => snap.data().count)); } diff --git a/firestore/lite/interfaces.ts b/firestore/lite/interfaces.ts index a4d6332..d4d64e2 100644 --- a/firestore/lite/interfaces.ts +++ b/firestore/lite/interfaces.ts @@ -2,10 +2,7 @@ import type * as lite from 'firebase/firestore/lite'; export type DocumentReference = lite.DocumentReference; export type DocumentData = lite.DocumentData; -export type Query< - AppModelType, - DbModelType extends DocumentData = DocumentData, -> = lite.Query; +export type Query = lite.Query; export type DocumentSnapshot = lite.DocumentSnapshot; export type QuerySnapshot = lite.QuerySnapshot; export type QueryDocumentSnapshot = lite.QueryDocumentSnapshot;