import {get, derived, Readable, Unsubscriber, Writable, Subscriber} from "svelte/store" import {memoize, identity, equals, throttle} from "@welshman/lib" // Define Stores and StoresValues types locally since they're not exported in Svelte 5 type Stores = Readable | [Readable, ...Array>] | Array> type StoresValues = T extends Readable ? U : {[K in keyof T]: T[K] extends Readable ? U : never} // Smart getter that adjusts between svelte's get and aggressive subscription depending on how hot // the path is export const getter = (store: Readable, {threshold = 10}: {threshold?: number} = {}) => { const calls: number[] = [] let unsubscribe: Unsubscriber | undefined let offset = 0 let value: T return () => { const now = Date.now() const cutoff = now - 1000 // Find the first timestamp within the window (avoid expensive shift) while (offset < calls.length && calls[offset] < cutoff) { offset++ } // Periodically clean up old timestamps to prevent unbounded growth if (offset > 100) { calls.splice(0, offset) offset = 0 } // Add current timestamp calls.push(now) // Check if call rate exceeds threshold and switch to more aggressive mode if (calls.length - offset > threshold) { if (!unsubscribe) { unsubscribe = store.subscribe((newValue: T) => { value = newValue }) } return value } else { if (unsubscribe) { unsubscribe() unsubscribe = undefined } return get(store) } } } export type WritableWithGetter = Writable & {get: () => T} export type ReadableWithGetter = Readable & {get: () => T} export function withGetter(store: Writable): WritableWithGetter export function withGetter(store: Readable): ReadableWithGetter export function withGetter(store: Readable | Writable) { return {...store, get: getter(store)} } export const memoized = (store: Readable) => { const {subscribe} = store return {...store, subscribe: (f: Subscriber) => subscribe(memoize(f))} } export const throttled = >(delay: number, store: S) => { if (delay) { const {subscribe} = store store = {...store, subscribe: (f: Subscriber) => subscribe(throttle(delay, f))} } return store } export const deriveDeduplicated = ( stores: S, get: (storeValues: StoresValues) => T, ): Readable => { let prev: T return derived(stores, (storeValues, set) => { const result = get(storeValues) if (prev !== result) { prev = result set(result) } }) } export const deriveDeduplicatedByValue = ( stores: S, get: (storeValues: StoresValues) => T, ): Readable => { let prev: T return derived(stores, (storeValues, set) => { const result = get(storeValues) if (!equals(prev, result)) { prev = result set(result) } }) } export const merged = (stores: S) => derived(stores, identity)