import { noop, subscribe, run_all, safe_not_equal, is_function } from '../internal/Component-cd97939e.mjs'; export { get_store_value as get } from '../internal/Component-cd97939e.mjs'; const subscriber_queue = []; /** * Creates a `Readable` store that allows reading by subscription. * @template T * @param {T} value initial value * @param {import('./public.js').StartStopNotifier} start * @returns {import('./public.js').Readable} */ function readable(value, start) { return { subscribe: writable(value, start).subscribe }; } /** * Create a `Writable` store that allows both updating and reading by subscription. * @template T * @param {T} value initial value * @param {import('./public.js').StartStopNotifier} start * @returns {import('./public.js').Writable} */ function writable(value, start = noop) { /** @type {import('./public.js').Unsubscriber} */ let stop; /** @type {Set>} */ const subscribers = new Set(); /** @param {T} new_value * @returns {void} */ function set(new_value) { if (safe_not_equal(value, new_value)) { value = new_value; if (stop) { // store is ready const run_queue = !subscriber_queue.length; for (const subscriber of subscribers) { subscriber[1](); subscriber_queue.push(subscriber, value); } if (run_queue) { for (let i = 0; i < subscriber_queue.length; i += 2) { subscriber_queue[i][0](subscriber_queue[i + 1]); } subscriber_queue.length = 0; } } } } /** * @param {import('./public.js').Updater} fn * @returns {void} */ function update(fn) { set(fn(value)); } /** * @param {import('./public.js').Subscriber} run * @param {import('./private.js').Invalidator} invalidate * @returns {import('./public.js').Unsubscriber} */ function subscribe(run, invalidate = noop) { /** @type {import('./private.js').SubscribeInvalidateTuple} */ const subscriber = [run, invalidate]; subscribers.add(subscriber); if (subscribers.size === 1) { stop = start(set, update) || noop; } run(value); return () => { subscribers.delete(subscriber); if (subscribers.size === 0 && stop) { stop(); stop = null; } }; } return { set, update, subscribe }; } /** * Derived value store by synchronizing one or more readable stores and * applying an aggregation function over its input values. * * @template {import('./private.js').Stores} S * @template T * @overload * @param {S} stores - input stores * @param {(values: import('./public.js').StoresValues, set: import('./public.js').Subscriber, update: (fn: import('./public.js').Updater) => void) => import('./public.js').Unsubscriber | void} fn - function callback that aggregates the values * @param {T} [initial_value] - initial value * @returns {import('./public.js').Readable} */ /** * Derived value store by synchronizing one or more readable stores and * applying an aggregation function over its input values. * * @template {import('./private.js').Stores} S * @template T * @overload * @param {S} stores - input stores * @param {(values: import('./public.js').StoresValues) => T} fn - function callback that aggregates the values * @param {T} [initial_value] - initial value * @returns {import('./public.js').Readable} */ /** * @template {import('./private.js').Stores} S * @template T * @param {S} stores * @param {Function} fn * @param {T} [initial_value] * @returns {import('./public.js').Readable} */ function derived(stores, fn, initial_value) { const single = !Array.isArray(stores); /** @type {Array>} */ const stores_array = single ? [stores] : stores; if (!stores_array.every(Boolean)) { throw new Error('derived() expects stores as input, got a falsy value'); } const auto = fn.length < 2; return readable(initial_value, (set, update) => { let started = false; const values = []; let pending = 0; let cleanup = noop; const sync = () => { if (pending) { return; } cleanup(); const result = fn(single ? values[0] : values, set, update); if (auto) { set(result); } else { cleanup = is_function(result) ? result : noop; } }; const unsubscribers = stores_array.map((store, i) => subscribe( store, (value) => { values[i] = value; pending &= ~(1 << i); if (started) { sync(); } }, () => { pending |= 1 << i; } ) ); started = true; sync(); return function stop() { run_all(unsubscribers); cleanup(); // We need to set this to false because callbacks can still happen despite having unsubscribed: // Callbacks might already be placed in the queue which doesn't know it should no longer // invoke this derived store. started = false; }; }); } /** * Takes a store and returns a new one derived from the old one that is readable. * * @template T * @param {import('./public.js').Readable} store - store to make readonly * @returns {import('./public.js').Readable} */ function readonly(store) { return { subscribe: store.subscribe.bind(store) }; } export { derived, readable, readonly, writable };