svelte-streamable
Version:
Super tiny, simple to use SvelteJS store for real-time updates from server via SSE.
147 lines (125 loc) • 3.64 kB
JavaScript
function noop$1() { }
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
Promise.resolve();
const subscriber_queue = [];
/**
* Creates a `Readable` store that allows reading by subscription.
* @param value initial value
* @param {StartStopNotifier}start start and stop notifications for subscriptions
*/
function readable(value, start) {
return {
subscribe: writable(value, start).subscribe
};
}
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
*/
function writable(value, start = noop$1) {
let stop;
const subscribers = new Set();
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;
}
}
}
}
function update(fn) {
set(fn(value));
}
function subscribe(run, invalidate = noop$1) {
const subscriber = [run, invalidate];
subscribers.add(subscriber);
if (subscribers.size === 1) {
stop = start(set) || noop$1;
}
run(value);
return () => {
subscribers.delete(subscriber);
if (subscribers.size === 0) {
stop();
stop = null;
}
};
}
return { set, update, subscribe };
}
const esx = {};
function streamable(
{ url, event = 'message', format = 'json', ...options },
callback,
defaultValue
) {
const auto = !callback || callback.length < 2;
const initial = defaultValue ? Promise.resolve(defaultValue) : new Promise(noop);
return readable(initial, (set) => {
let cleanup = noop;
function resolve(value) {
set(typeof value !== 'undefined' ? Promise.resolve(value) : initial);
}
function update(e) {
cleanup(false);
let data;
if (e && e.data) {
if (format === 'json') {
data = JSON.parse(e.data);
} else if (format === 'base64') {
data = atob(e.data);
} else if (format === 'urlencoded') {
data = Object.fromEntries(new URLSearchParams(e.data));
} else {
data = e.data;
}
}
const result = callback ? callback(data, resolve) : data;
if (auto) {
resolve(result);
} else {
cleanup = typeof result === 'function' ? result : noop;
}
}
function error(e) {
set(Promise.reject(e));
}
const keypath = Object.entries(options)
.sort()
.reduce((k, [, v]) => `${k}/${v}`, url);
let es = esx[keypath];
if (!es) {
es = new EventSource(url, options);
es.subscribers = 0;
esx[keypath] = es;
}
es.addEventListener('error', error);
es.addEventListener(event, update);
callback && setTimeout(update);
es.subscribers++;
return () => {
es.removeEventListener('error', error);
es.removeEventListener(event, update);
if (!--es.subscribers) {
es.close();
delete esx[keypath];
}
cleanup(true);
};
});
}
function noop() {}
export { streamable };