this repo has no description
at main 199 lines 5.4 kB view raw
1import { 2 run_all, 3 subscribe, 4 noop, 5 safe_not_equal, 6 is_function, 7 get_store_value 8} from '../internal/index.js'; 9 10const subscriber_queue = []; 11 12/** 13 * Creates a `Readable` store that allows reading by subscription. 14 * 15 * https://svelte.dev/docs/svelte-store#readable 16 * @template T 17 * @param {T} [value] initial value 18 * @param {import('./public.js').StartStopNotifier<T>} [start] 19 * @returns {import('./public.js').Readable<T>} 20 */ 21export function readable(value, start) { 22 return { 23 subscribe: writable(value, start).subscribe 24 }; 25} 26 27/** 28 * Create a `Writable` store that allows both updating and reading by subscription. 29 * 30 * https://svelte.dev/docs/svelte-store#writable 31 * @template T 32 * @param {T} [value] initial value 33 * @param {import('./public.js').StartStopNotifier<T>} [start] 34 * @returns {import('./public.js').Writable<T>} 35 */ 36export function writable(value, start = noop) { 37 /** @type {import('./public.js').Unsubscriber} */ 38 let stop; 39 /** @type {Set<import('./private.js').SubscribeInvalidateTuple<T>>} */ 40 const subscribers = new Set(); 41 /** @param {T} new_value 42 * @returns {void} 43 */ 44 function set(new_value) { 45 if (safe_not_equal(value, new_value)) { 46 value = new_value; 47 if (stop) { 48 // store is ready 49 const run_queue = !subscriber_queue.length; 50 for (const subscriber of subscribers) { 51 subscriber[1](); 52 subscriber_queue.push(subscriber, value); 53 } 54 if (run_queue) { 55 for (let i = 0; i < subscriber_queue.length; i += 2) { 56 subscriber_queue[i][0](subscriber_queue[i + 1]); 57 } 58 subscriber_queue.length = 0; 59 } 60 } 61 } 62 } 63 64 /** 65 * @param {import('./public.js').Updater<T>} fn 66 * @returns {void} 67 */ 68 function update(fn) { 69 set(fn(value)); 70 } 71 72 /** 73 * @param {import('./public.js').Subscriber<T>} run 74 * @param {import('./private.js').Invalidator<T>} [invalidate] 75 * @returns {import('./public.js').Unsubscriber} 76 */ 77 function subscribe(run, invalidate = noop) { 78 /** @type {import('./private.js').SubscribeInvalidateTuple<T>} */ 79 const subscriber = [run, invalidate]; 80 subscribers.add(subscriber); 81 if (subscribers.size === 1) { 82 stop = start(set, update) || noop; 83 } 84 run(value); 85 return () => { 86 subscribers.delete(subscriber); 87 if (subscribers.size === 0 && stop) { 88 stop(); 89 stop = null; 90 } 91 }; 92 } 93 return { set, update, subscribe }; 94} 95 96/** 97 * Derived value store by synchronizing one or more readable stores and 98 * applying an aggregation function over its input values. 99 * 100 * https://svelte.dev/docs/svelte-store#derived 101 * @template {import('./private.js').Stores} S 102 * @template T 103 * @overload 104 * @param {S} stores - input stores 105 * @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 106 * @param {T} [initial_value] - initial value 107 * @returns {import('./public.js').Readable<T>} 108 */ 109 110/** 111 * Derived value store by synchronizing one or more readable stores and 112 * applying an aggregation function over its input values. 113 * 114 * https://svelte.dev/docs/svelte-store#derived 115 * @template {import('./private.js').Stores} S 116 * @template T 117 * @overload 118 * @param {S} stores - input stores 119 * @param {(values: import('./private.js').StoresValues<S>) => T} fn - function callback that aggregates the values 120 * @param {T} [initial_value] - initial value 121 * @returns {import('./public.js').Readable<T>} 122 */ 123 124/** 125 * @template {import('./private.js').Stores} S 126 * @template T 127 * @param {S} stores 128 * @param {Function} fn 129 * @param {T} [initial_value] 130 * @returns {import('./public.js').Readable<T>} 131 */ 132export function derived(stores, fn, initial_value) { 133 const single = !Array.isArray(stores); 134 /** @type {Array<import('./public.js').Readable<any>>} */ 135 const stores_array = single ? [stores] : stores; 136 if (!stores_array.every(Boolean)) { 137 throw new Error('derived() expects stores as input, got a falsy value'); 138 } 139 const auto = fn.length < 2; 140 return readable(initial_value, (set, update) => { 141 let started = false; 142 const values = []; 143 let pending = 0; 144 let cleanup = noop; 145 const sync = () => { 146 if (pending) { 147 return; 148 } 149 cleanup(); 150 const result = fn(single ? values[0] : values, set, update); 151 if (auto) { 152 set(result); 153 } else { 154 cleanup = is_function(result) ? result : noop; 155 } 156 }; 157 const unsubscribers = stores_array.map((store, i) => 158 subscribe( 159 store, 160 (value) => { 161 values[i] = value; 162 pending &= ~(1 << i); 163 if (started) { 164 sync(); 165 } 166 }, 167 () => { 168 pending |= 1 << i; 169 } 170 ) 171 ); 172 started = true; 173 sync(); 174 return function stop() { 175 run_all(unsubscribers); 176 cleanup(); 177 // We need to set this to false because callbacks can still happen despite having unsubscribed: 178 // Callbacks might already be placed in the queue which doesn't know it should no longer 179 // invoke this derived store. 180 started = false; 181 }; 182 }); 183} 184 185/** 186 * Takes a store and returns a new one derived from the old one that is readable. 187 * 188 * https://svelte.dev/docs/svelte-store#readonly 189 * @template T 190 * @param {import('./public.js').Readable<T>} store - store to make readonly 191 * @returns {import('./public.js').Readable<T>} 192 */ 193export function readonly(store) { 194 return { 195 subscribe: store.subscribe.bind(store) 196 }; 197} 198 199export { get_store_value as get };