UNPKG

@angular/core

Version:

Angular - the core framework

261 lines (251 loc) 7.96 kB
/** * @license Angular v21.0.6 * (c) 2010-2025 Google LLC. https://angular.dev/ * License: MIT */ import { Observable, ReplaySubject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { assertInInjectionContext, inject, DestroyRef, RuntimeError, Injector, effect, untracked, assertNotInReactiveContext, signal, PendingTasks } from './_untracked-chunk.mjs'; import { getOutputDestroyRef, computed, resource, encapsulateResourceError } from './_resource-chunk.mjs'; import './_effect-chunk.mjs'; import './_not_found-chunk.mjs'; import '@angular/core/primitives/signals'; import '@angular/core/primitives/di'; import './_linked_signal-chunk.mjs'; function takeUntilDestroyed(destroyRef) { if (!destroyRef) { ngDevMode && assertInInjectionContext(takeUntilDestroyed); destroyRef = inject(DestroyRef); } const destroyed$ = new Observable(subscriber => { if (destroyRef.destroyed) { subscriber.next(); return; } const unregisterFn = destroyRef.onDestroy(subscriber.next.bind(subscriber)); return unregisterFn; }); return source => { return source.pipe(takeUntil(destroyed$)); }; } class OutputFromObservableRef { source; destroyed = false; destroyRef = inject(DestroyRef); constructor(source) { this.source = source; this.destroyRef.onDestroy(() => { this.destroyed = true; }); } subscribe(callbackFn) { if (this.destroyed) { throw new RuntimeError(953, ngDevMode && 'Unexpected subscription to destroyed `OutputRef`. ' + 'The owning directive/component is destroyed.'); } const subscription = this.source.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({ next: value => callbackFn(value) }); return { unsubscribe: () => subscription.unsubscribe() }; } } function outputFromObservable(observable, opts) { ngDevMode && assertInInjectionContext(outputFromObservable); return new OutputFromObservableRef(observable); } function outputToObservable(ref) { const destroyRef = getOutputDestroyRef(ref); return new Observable(observer => { const unregisterOnDestroy = destroyRef?.onDestroy(() => observer.complete()); const subscription = ref.subscribe(v => observer.next(v)); return () => { subscription.unsubscribe(); unregisterOnDestroy?.(); }; }); } function toObservable(source, options) { if (ngDevMode && !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(); } function toSignal(source, options) { typeof ngDevMode !== 'undefined' && 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; if (ngDevMode && requiresCleanup && !options?.injector) { assertInInjectionContext(toSignal); } const cleanupRef = requiresCleanup ? options?.injector?.get(DestroyRef) ?? inject(DestroyRef) : null; const equal = makeToSignalEqual(options?.equal); let state; if (options?.requireSync) { state = signal({ kind: 0 }, { equal, ...(ngDevMode ? createDebugNameObject(options?.debugName, 'state') : undefined) }); } else { state = signal({ kind: 1, value: options?.initialValue }, { equal, ...(ngDevMode ? createDebugNameObject(options?.debugName, 'state') : undefined) }); } let destroyUnregisterFn; const sub = source.subscribe({ next: value => state.set({ kind: 1, value }), error: error => { state.set({ kind: 2, error }); destroyUnregisterFn?.(); }, complete: () => { destroyUnregisterFn?.(); } }); if (options?.requireSync && state().kind === 0) { throw new RuntimeError(601, (typeof ngDevMode === 'undefined' || ngDevMode) && '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.'); } destroyUnregisterFn = cleanupRef?.onDestroy(sub.unsubscribe.bind(sub)); return computed(() => { const current = state(); switch (current.kind) { case 1: return current.value; case 2: throw current.error; case 0: throw new RuntimeError(601, (typeof ngDevMode === 'undefined' || ngDevMode) && '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.'); } }, { equal: options?.equal, ...(ngDevMode ? createDebugNameObject(options?.debugName, 'source') : undefined) }); } function makeToSignalEqual(userEquality = Object.is) { return (a, b) => a.kind === 1 && b.kind === 1 && userEquality(a.value, b.value); } function createDebugNameObject(toSignalDebugName, internalSignalDebugName) { return { debugName: `toSignal${toSignalDebugName ? '#' + toSignalDebugName : ''}.${internalSignalDebugName}` }; } function pendingUntilEvent(injector) { if (injector === undefined) { ngDevMode && assertInInjectionContext(pendingUntilEvent); injector = inject(Injector); } const taskService = injector.get(PendingTasks); return sourceObservable => { return new Observable(originalSubscriber => { const removeTask = taskService.add(); let cleanedUp = false; function cleanupTask() { if (cleanedUp) { return; } removeTask(); cleanedUp = true; } const innerSubscription = sourceObservable.subscribe({ next: v => { originalSubscriber.next(v); cleanupTask(); }, complete: () => { originalSubscriber.complete(); cleanupTask(); }, error: e => { originalSubscriber.error(e); cleanupTask(); } }); innerSubscription.add(() => { originalSubscriber.unsubscribe(); cleanupTask(); }); return innerSubscription; }); }; } function rxResource(opts) { if (ngDevMode && !opts?.injector) { assertInInjectionContext(rxResource); } return resource({ ...opts, loader: undefined, stream: params => { let sub; const onAbort = () => sub?.unsubscribe(); params.abortSignal.addEventListener('abort', onAbort); const stream = signal({ value: undefined }); let resolve; const promise = new Promise(r => resolve = r); function send(value) { stream.set(value); resolve?.(stream); resolve = undefined; } const streamFn = opts.stream ?? opts.loader; if (streamFn === undefined) { throw new RuntimeError(990, ngDevMode && `Must provide \`stream\` option.`); } sub = streamFn(params).subscribe({ next: value => send({ value }), error: error => { send({ error: encapsulateResourceError(error) }); params.abortSignal.removeEventListener('abort', onAbort); }, complete: () => { if (resolve) { send({ error: new RuntimeError(991, ngDevMode && 'Resource completed before producing a value') }); } params.abortSignal.removeEventListener('abort', onAbort); } }); return promise; } }); } export { outputFromObservable, outputToObservable, pendingUntilEvent, rxResource, takeUntilDestroyed, toObservable, toSignal }; //# sourceMappingURL=rxjs-interop.mjs.map