/** * @license Angular v18.2.10 * (c) 2010-2024 Google LLC. https://angular.io/ * License: MIT */ import { assertInInjectionContext, inject, DestroyRef, ɵRuntimeError, ɵgetOutputDestroyRef, Injector, effect, untracked, assertNotInReactiveContext, signal, computed } from '@angular/core'; import { Observable, ReplaySubject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; /** * Operator which completes the Observable when the calling context (component, directive, service, * etc) is destroyed. * * @param destroyRef optionally, the `DestroyRef` representing the current context. This can be * passed explicitly to use `takeUntilDestroyed` outside of an [injection * context](guide/di/dependency-injection-context). Otherwise, the current `DestroyRef` is injected. * * @developerPreview */ function takeUntilDestroyed(destroyRef) { if (!destroyRef) { assertInInjectionContext(takeUntilDestroyed); destroyRef = inject(DestroyRef); } const destroyed$ = new Observable((observer) => { const unregisterFn = destroyRef.onDestroy(observer.next.bind(observer)); return unregisterFn; }); return (source) => { return source.pipe(takeUntil(destroyed$)); }; } /** * Implementation of `OutputRef` that emits values from * an RxJS observable source. * * @internal */ class OutputFromObservableRef { constructor(source) { this.source = source; this.destroyed = false; this.destroyRef = inject(DestroyRef); this.destroyRef.onDestroy(() => { this.destroyed = true; }); } subscribe(callbackFn) { if (this.destroyed) { throw new ɵRuntimeError(953 /* ɵRuntimeErrorCode.OUTPUT_REF_DESTROYED */, ngDevMode && 'Unexpected subscription to destroyed `OutputRef`. ' + 'The owning directive/component is destroyed.'); } // Stop yielding more values when the directive/component is already destroyed. const subscription = this.source.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({ next: (value) => callbackFn(value), }); return { unsubscribe: () => subscription.unsubscribe(), }; } } /** * Declares an Angular output that is using an RxJS observable as a source * for events dispatched to parent subscribers. * * The behavior for an observable as source is defined as followed: * 1. New values are forwarded to the Angular output (next notifications). * 2. Errors notifications are not handled by Angular. You need to handle these manually. * For example by using `catchError`. * 3. Completion notifications stop the output from emitting new values. * * @usageNotes * Initialize an output in your directive by declaring a * class field and initializing it with the `outputFromObservable()` function. * * ```ts * @Directive({..}) * export class MyDir { * nameChange$ = ; * nameChange = outputFromObservable(this.nameChange$); * } * ``` * * @developerPreview */ function outputFromObservable(observable, opts) { ngDevMode && assertInInjectionContext(outputFromObservable); return new OutputFromObservableRef(observable); } /** * Converts an Angular output declared via `output()` or `outputFromObservable()` * to an observable. * * You can subscribe to the output via `Observable.subscribe` then. * * @developerPreview */ function outputToObservable(ref) { const destroyRef = ɵgetOutputDestroyRef(ref); return new Observable((observer) => { // Complete the observable upon directive/component destroy. // Note: May be `undefined` if an `EventEmitter` is declared outside // of an injection context. destroyRef?.onDestroy(() => observer.complete()); const subscription = ref.subscribe((v) => observer.next(v)); return () => subscription.unsubscribe(); }); } /** * Exposes the value of an Angular `Signal` as an RxJS `Observable`. * * The signal's value will be propagated into the `Observable`'s subscribers using an `effect`. * * `toObservable` must be called in an injection context unless an injector is provided via options. * * @developerPreview */ function toObservable(source, options) { !options?.injector && assertInInjectionContext(toObservable); const injector = options?.injector ?? inject(Injector); const subject = new ReplaySubject(1); const watcher = effect(() => { let value; try { value = source(); } catch (err) { untracked(() => subject.error(err)); return; } untracked(() => subject.next(value)); }, { injector, manualCleanup: true }); injector.get(DestroyRef).onDestroy(() => { watcher.destroy(); subject.complete(); }); return subject.asObservable(); } /** * Get the current value of an `Observable` as a reactive `Signal`. * * `toSignal` returns a `Signal` which provides synchronous reactive access to values produced * by the given `Observable`, by subscribing to that `Observable`. The returned `Signal` will always * have the most recent value emitted by the subscription, and will throw an error if the * `Observable` errors. * * With `requireSync` set to `true`, `toSignal` will assert that the `Observable` produces a value * immediately upon subscription. No `initialValue` is needed in this case, and the returned signal * does not include an `undefined` type. * * By default, the subscription will be automatically cleaned up when the current [injection * context](guide/di/dependency-injection-context) is destroyed. For example, when `toSignal` is * called during the construction of a component, the subscription will be cleaned up when the * component is destroyed. If an injection context is not available, an explicit `Injector` can be * passed instead. * * If the subscription should persist until the `Observable` itself completes, the `manualCleanup` * option can be specified instead, which disables the automatic subscription teardown. No injection * context is needed in this configuration as well. * * @developerPreview */ function toSignal(source, options) { ngDevMode && assertNotInReactiveContext(toSignal, 'Invoking `toSignal` causes new subscriptions every time. ' + 'Consider moving `toSignal` outside of the reactive context and read the signal value where needed.'); const requiresCleanup = !options?.manualCleanup; requiresCleanup && !options?.injector && assertInInjectionContext(toSignal); const cleanupRef = requiresCleanup ? (options?.injector?.get(DestroyRef) ?? inject(DestroyRef)) : null; const equal = makeToSignalEqual(options?.equal); // Note: T is the Observable value type, and U is the initial value type. They don't have to be // the same - the returned signal gives values of type `T`. let state; if (options?.requireSync) { // Initially the signal is in a `NoValue` state. state = signal({ kind: 0 /* StateKind.NoValue */ }, { equal }); } else { // If an initial value was passed, use it. Otherwise, use `undefined` as the initial value. state = signal({ kind: 1 /* StateKind.Value */, value: options?.initialValue }, { equal }); } // Note: This code cannot run inside a reactive context (see assertion above). If we'd support // this, we would subscribe to the observable outside of the current reactive context, avoiding // that side-effect signal reads/writes are attribute to the current consumer. The current // consumer only needs to be notified when the `state` signal changes through the observable // subscription. Additional context (related to async pipe): // https://github.com/angular/angular/pull/50522. const sub = source.subscribe({ next: (value) => state.set({ kind: 1 /* StateKind.Value */, value }), error: (error) => { if (options?.rejectErrors) { // Kick the error back to RxJS. It will be caught and rethrown in a macrotask, which causes // the error to end up as an uncaught exception. throw error; } state.set({ kind: 2 /* StateKind.Error */, error }); }, // Completion of the Observable is meaningless to the signal. Signals don't have a concept of // "complete". }); if (options?.requireSync && state().kind === 0 /* StateKind.NoValue */) { throw new ɵRuntimeError(601 /* ɵRuntimeErrorCode.REQUIRE_SYNC_WITHOUT_SYNC_EMIT */, (typeof ngDevMode === 'undefined' || ngDevMode) && '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.'); } // Unsubscribe when the current context is destroyed, if requested. cleanupRef?.onDestroy(sub.unsubscribe.bind(sub)); // The actual returned signal is a `computed` of the `State` signal, which maps the various states // to either values or errors. return computed(() => { const current = state(); switch (current.kind) { case 1 /* StateKind.Value */: return current.value; case 2 /* StateKind.Error */: throw current.error; case 0 /* StateKind.NoValue */: // This shouldn't really happen because the error is thrown on creation. throw new ɵRuntimeError(601 /* ɵRuntimeErrorCode.REQUIRE_SYNC_WITHOUT_SYNC_EMIT */, (typeof ngDevMode === 'undefined' || ngDevMode) && '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.'); } }, { equal: options?.equal }); } function makeToSignalEqual(userEquality = Object.is) { return (a, b) => a.kind === 1 /* StateKind.Value */ && b.kind === 1 /* StateKind.Value */ && userEquality(a.value, b.value); } /** * Generated bundle index. Do not edit. */ export { outputFromObservable, outputToObservable, takeUntilDestroyed, toObservable, toSignal }; //# sourceMappingURL=rxjs-interop.mjs.map