@angular/core
Version:
Angular - the core framework
261 lines (251 loc) • 7.96 kB
JavaScript
/**
* @license Angular v21.0.5
* (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