44Injector ,
55NgZone ,
66PendingTasks ,
7- inject
7+ inject ,
8+ isDevMode ,
9+ runInInjectionContext
810} from '@angular/core' ;
911import { pendingUntilEvent } from '@angular/core/rxjs-interop' ;
1012import {
@@ -76,31 +78,71 @@ function getSchedulers() {
7678return inject ( ɵAngularFireSchedulers ) ;
7779}
7880
81+ var alreadyWarned = false ;
82+ function warnOutsideInjectionContext ( original :any , operation :string ) {
83+ if ( isDevMode ( ) ) {
84+ console . warn ( `Firebase API called outside injection context:${ operation } (${ original . name } )` ) ;
85+ if ( ! alreadyWarned ) {
86+ alreadyWarned = true ;
87+ console . error ( "Calling Firebase APIs outside of an Injection context may destabilize your application leading to subtle change-detection and hydration bugs. Find more at https://github.com/angular/angularfire/blob/main/docs/zones.md" ) ;
88+ }
89+ }
90+ }
91+
7992function runOutsideAngular < T > ( fn :( ...args :any [ ] ) => T ) :T {
80- return inject ( NgZone ) . runOutsideAngular ( ( ) => fn ( ) ) ;
93+ let ngZone :NgZone | undefined ;
94+ try {
95+ ngZone = inject ( NgZone ) ;
96+ } catch ( e ) {
97+ warnOutsideInjectionContext ( fn , "runOutsideAngular" ) ;
98+ }
99+ if ( ! ngZone ) { return fn ( ) ; }
100+ return ngZone . runOutsideAngular ( ( ) => fn ( ) ) ;
81101}
82102
83103function run < T > ( fn :( ...args :any [ ] ) => T ) :T {
84- return inject ( NgZone ) . run ( ( ) => fn ( ) ) ;
104+ let ngZone :NgZone | undefined ;
105+ try {
106+ ngZone = inject ( NgZone ) ;
107+ } catch ( e ) {
108+ warnOutsideInjectionContext ( fn , "run" ) ;
109+ }
110+ if ( ! ngZone ) { return fn ( ) ; }
111+ return ngZone . run ( ( ) => fn ( ) ) ;
85112}
86113
87114export function observeOutsideAngular < T > ( obs$ :Observable < T > ) :Observable < T > {
88- return obs$ . pipe ( observeOn ( getSchedulers ( ) . outsideAngular ) ) ;
115+ let schedulers :ɵAngularFireSchedulers | undefined ;
116+ try {
117+ schedulers = getSchedulers ( ) ;
118+ } catch ( e ) {
119+ warnOutsideInjectionContext ( obs$ , "observeOutsideAngular" ) ;
120+ }
121+ if ( ! schedulers ) { return obs$ ; }
122+ return obs$ . pipe ( observeOn ( schedulers . outsideAngular ) ) ;
89123}
90124
91125export function observeInsideAngular < T > ( obs$ :Observable < T > ) :Observable < T > {
92- return obs$ . pipe ( observeOn ( getSchedulers ( ) . insideAngular ) ) ;
126+ let schedulers :ɵAngularFireSchedulers | undefined ;
127+ try {
128+ schedulers = getSchedulers ( ) ;
129+ } catch ( e ) {
130+ warnOutsideInjectionContext ( obs$ , "observeInsideAngular" ) ;
131+ }
132+ if ( ! schedulers ) { return obs$ ; }
133+ return obs$ . pipe ( observeOn ( schedulers . insideAngular ) ) ;
93134}
94135
95136const zoneWrapFn = (
96137it :( ...args :any [ ] ) => any ,
97- taskDone :VoidFunction | undefined
138+ taskDone :VoidFunction | undefined ,
139+ injector :Injector ,
98140) => {
99141return ( ...args :any [ ] ) => {
100142if ( taskDone ) {
101143setTimeout ( taskDone , 0 ) ;
102144}
103- return run ( ( ) => it . apply ( this , args ) ) ;
145+ return runInInjectionContext ( injector , ( ) => run ( ( ) => it . apply ( this , args ) ) ) ;
104146} ;
105147} ;
106148
@@ -117,6 +159,7 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
117159pendingTasks = inject ( PendingTasks ) ;
118160injector = inject ( Injector ) ;
119161} catch ( e ) {
162+ warnOutsideInjectionContext ( it , "ɵzoneWrap" ) ;
120163return ( it as any ) . apply ( this , _arguments ) ;
121164}
122165// if this is a callback function, e.g, onSnapshot, we should create a pending task and complete it
@@ -127,7 +170,7 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
127170taskDone ||= run ( ( ) => pendingTasks . add ( ) ) ;
128171}
129172// TODO create a microtask to track callback functions
130- _arguments [ i ] = zoneWrapFn ( _arguments [ i ] , taskDone ) ;
173+ _arguments [ i ] = zoneWrapFn ( _arguments [ i ] , taskDone , injector ) ;
131174}
132175}
133176const ret = runOutsideAngular ( ( ) => ( it as any ) . apply ( this , _arguments ) ) ;
@@ -153,8 +196,8 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
153196( ) =>
154197new Promise ( ( resolve , reject ) => {
155198pendingTasks . run ( ( ) => ret ) . then (
156- ( it ) => run ( ( ) => resolve ( it ) ) ,
157- ( reason ) => run ( ( ) => reject ( reason ) )
199+ ( it ) => runInInjectionContext ( injector , ( ) => run ( ( ) => resolve ( it ) ) ) ,
200+ ( reason ) => runInInjectionContext ( injector , ( ) => run ( ( ) => reject ( reason ) ) )
158201) ;
159202} )
160203) ;