UNPKG

rxdb

Version:

A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/

86 lines (76 loc) 3.02 kB
import { signal, Signal } from "@preact/signals-core"; import type { Observable, Subscription } from 'rxjs'; import type { WeakRef, FinalizationRegistry, RxReactivityFactory, ReactivityLambda } from '../../types/index.d.ts'; /** * Type-level function (ReactivityLambda) for Preact signals. * Use this as the Reactivity type parameter for properly typed signals. * * @example * const db = await createRxDatabase<MyCollections, any, any, PreactSignalReactivityLambda>({ * reactivity: PreactSignalsRxReactivityFactory * }); * const signal = doc.age$$; // Signal<number> */ export interface PreactSignalReactivityLambda extends ReactivityLambda { readonly _result: Signal<this['_data']>; } export type PreactSignal<T = any> = Signal<T>; /** * Exported to debug and test * the behavior */ export const PREACT_SIGNAL_STATE = { subscribeCount: 0, signalBySubscription: new WeakMap<Subscription, PreactSignal>(), aliveSubscription: new Set<Subscription>(), }; function removeSubscription(sub: Subscription) { const isAlive = PREACT_SIGNAL_STATE.aliveSubscription.has(sub); if (isAlive) { PREACT_SIGNAL_STATE.aliveSubscription.delete(sub); PREACT_SIGNAL_STATE.signalBySubscription.delete(sub); PREACT_SIGNAL_STATE.subscribeCount = PREACT_SIGNAL_STATE.subscribeCount - 1; sub.unsubscribe(); } } function cleanupCallback(sub: Subscription) { // called when a signal becomes garbage collected removeSubscription(sub); } const cleanupRegistry: FinalizationRegistry<Subscription> = new FinalizationRegistry(cleanupCallback) as any; export const PreactSignalsRxReactivityFactory: RxReactivityFactory<PreactSignalReactivityLambda> = { fromObservable<Data, InitData>( obs: Observable<Data>, initialValue: InitData ): PreactSignal<Data | InitData> { const mySignal = signal<Data | InitData>(initialValue); const sigRef: WeakRef = new WeakRef(mySignal); const sub = obs.subscribe((value: Data) => { const sig = PREACT_SIGNAL_STATE.signalBySubscription.get(sub); if (sig && sigRef.deref()) { sig.value = value; } else { /** * The callback of FinalizationRegistry is not reliable by definition * so we have this fallback to still clean unused signals subscriptions * when they emit but the signal is already not used anymore. * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry#notes_on_cleanup_callbacks */ removeSubscription(sub); } }); PREACT_SIGNAL_STATE.aliveSubscription.add(sub); PREACT_SIGNAL_STATE.signalBySubscription.set(sub, mySignal); PREACT_SIGNAL_STATE.subscribeCount = PREACT_SIGNAL_STATE.subscribeCount + 1; cleanupRegistry.register({}, sub); return mySignal; } };