import { | |
run_all, | |
subscribe, | |
noop, | |
safe_not_equal, | |
is_function, | |
get_store_value | |
} from '../internal/index.js'; | |
const subscriber_queue = []; | |
/** | |
* Creates a `Readable` store that allows reading by subscription. | |
* | |
* https://svelte.dev/docs/svelte-store#readable | |
* @template T | |
* @param {T} [value] initial value | |
* @param {import('./public.js').StartStopNotifier<T>} [start] | |
* @returns {import('./public.js').Readable<T>} | |
*/ | |
export function readable(value, start) { | |
return { | |
subscribe: writable(value, start).subscribe | |
}; | |
} | |
/** | |
* Create a `Writable` store that allows both updating and reading by subscription. | |
* | |
* https://svelte.dev/docs/svelte-store#writable | |
* @template T | |
* @param {T} [value] initial value | |
* @param {import('./public.js').StartStopNotifier<T>} [start] | |
* @returns {import('./public.js').Writable<T>} | |
*/ | |
export function writable(value, start = noop) { | |
/** @type {import('./public.js').Unsubscriber} */ | |
let stop; | |
/** @type {Set<import('./private.js').SubscribeInvalidateTuple<T>>} */ | |
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<T>} fn | |
* @returns {void} | |
*/ | |
function update(fn) { | |
set(fn(value)); | |
} | |
/** | |
* @param {import('./public.js').Subscriber<T>} run | |
* @param {import('./private.js').Invalidator<T>} [invalidate] | |
* @returns {import('./public.js').Unsubscriber} | |
*/ | |
function subscribe(run, invalidate = noop) { | |
/** @type {import('./private.js').SubscribeInvalidateTuple<T>} */ | |
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. | |
* | |
* https://svelte.dev/docs/svelte-store#derived | |
* @template {import('./private.js').Stores} S | |
* @template T | |
* @overload | |
* @param {S} stores - input stores | |
* @param {(values: import('./private.js').StoresValues<S>, set: (value: T) => void, update: (fn: import('./public.js').Updater<T>) => void) => import('./public.js').Unsubscriber | void} fn - function callback that aggregates the values | |
* @param {T} [initial_value] - initial value | |
* @returns {import('./public.js').Readable<T>} | |
*/ | |
/** | |
* Derived value store by synchronizing one or more readable stores and | |
* applying an aggregation function over its input values. | |
* | |
* https://svelte.dev/docs/svelte-store#derived | |
* @template {import('./private.js').Stores} S | |
* @template T | |
* @overload | |
* @param {S} stores - input stores | |
* @param {(values: import('./private.js').StoresValues<S>) => T} fn - function callback that aggregates the values | |
* @param {T} [initial_value] - initial value | |
* @returns {import('./public.js').Readable<T>} | |
*/ | |
/** | |
* @template {import('./private.js').Stores} S | |
* @template T | |
* @param {S} stores | |
* @param {Function} fn | |
* @param {T} [initial_value] | |
* @returns {import('./public.js').Readable<T>} | |
*/ | |
export function derived(stores, fn, initial_value) { | |
const single = !Array.isArray(stores); | |
/** @type {Array<import('./public.js').Readable<any>>} */ | |
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. | |
* | |
* https://svelte.dev/docs/svelte-store#readonly | |
* @template T | |
* @param {import('./public.js').Readable<T>} store - store to make readonly | |
* @returns {import('./public.js').Readable<T>} | |
*/ | |
export function readonly(store) { | |
return { | |
subscribe: store.subscribe.bind(store) | |
}; | |
} | |
export { get_store_value as get }; | |