valync
Version:
**A lightweight, framework-agnostic async data handling library for React & Vue, inspired by Riverpod’s AsyncValue pattern and powered by ts-results-es.**
201 lines • 7.42 kB
JavaScript
import { ref, onMounted, watch } from "vue";
import { Some, None } from "ts-results-es";
import { normalizeKey, AsyncLoading, AsyncError, AsyncData, } from "../core";
const cache = new Map();
export function createValyn({ client, }) {
return function (key, options = {}) {
const keyStr = normalizeKey(key);
const controller = ref();
let initialValue;
if (options.initialData) {
initialValue =
options.initialData.status === "success"
? new AsyncData(Some(options.initialData.data))
: new AsyncError(options.initialData.error);
}
else if (options.cache !== false && cache.has(keyStr)) {
initialValue = cache.get(keyStr);
}
else {
initialValue = new AsyncData(None);
}
const state = ref(initialValue);
const isClient = typeof window !== "undefined" &&
typeof AbortController !== "undefined";
const doFetch = () => {
controller.value?.abort();
controller.value = new AbortController();
if (options.cache !== false && cache.has(keyStr)) {
state.value = cache.get(keyStr);
return;
}
state.value = new AsyncLoading();
const attempt = (tries) => {
client(typeof key === "string" ? key : keyStr, {
...options.init,
signal: controller.value.signal,
})
.then((res) => {
if (controller.value.signal.aborted)
return;
if (res.status === "failed")
state.value = new AsyncError(res.error);
else {
const data = options.onData
? options.onData(res.data)
: res.data;
const sd = new AsyncData(Some(data));
if (options.cache !== false)
cache.set(keyStr, sd);
state.value = sd;
}
})
.catch((err) => {
if (controller.value.signal.aborted)
return;
if (tries > 0)
return attempt(tries - 1);
state.value = new AsyncError({
name: "NetworkError",
message: err.message,
code: "500",
});
});
};
attempt(options.retryCount ?? 0);
};
if (isClient &&
options.fetchOnMount !== false &&
!options.initialData) {
onMounted(doFetch);
}
if (isClient && options.watch && options.watch.length > 0) {
watch(options.watch, doFetch);
}
const refetch = () => {
if (isClient)
doFetch();
};
const setData = (updater) => {
const currentVal = state.value instanceof AsyncData
? state.value.value.isSome()
? state.value.value.unwrap()
: null
: null;
const newVal = new AsyncData(Some(updater(currentVal)));
if (options.cache !== false)
cache.set(keyStr, newVal);
state.value = newVal;
};
return [state.value, refetch, setData];
};
}
export function useValync(key, options = {}) {
const keyStr = normalizeKey(key);
const controller = ref();
let initialValue;
if (options.initialData) {
initialValue =
options.initialData.status === "success"
? new AsyncData(Some(options.initialData.data))
: new AsyncError(options.initialData.error);
}
else if (options.cache !== false && cache.has(keyStr)) {
initialValue = cache.get(keyStr);
}
else {
initialValue = new AsyncData(None);
}
const state = ref(initialValue);
const isClient = typeof window !== "undefined" && typeof AbortController !== "undefined";
const doFetch = () => {
controller.value?.abort();
controller.value = new AbortController();
if (options.cache !== false && cache.has(keyStr)) {
state.value = cache.get(keyStr);
return;
}
state.value = new AsyncLoading();
const attempt = (tries) => {
fetch(typeof key === "string" ? key : keyStr, {
...options.init,
signal: controller.value.signal,
})
.then(async (resp) => {
let json;
try {
json = await resp.json();
}
catch {
return {
status: "failed",
error: {
name: "ParseError",
message: "Invalid JSON",
},
};
}
if (!resp.ok || json.status === "failed") {
return {
status: "failed",
error: json.error ?? {
name: "HttpError",
message: resp.statusText,
code: resp.status,
},
};
}
return json;
})
.then((res) => {
if (controller.value.signal.aborted)
return;
if (res.status === "failed")
state.value = new AsyncError(res.error);
else {
const data = options.onData
? options.onData(res.data)
: res.data;
const sd = new AsyncData(Some(data));
if (options.cache !== false)
cache.set(keyStr, sd);
state.value = sd;
}
})
.catch((err) => {
if (controller.value.signal.aborted)
return;
if (tries > 0)
return attempt(tries - 1);
state.value = new AsyncError({
name: "NetworkError",
message: err.message,
});
});
};
attempt(options.retryCount ?? 0);
};
if (isClient && options.fetchOnMount !== false && !options.initialData) {
onMounted(doFetch);
}
if (isClient && options.watch && options.watch.length > 0) {
watch(options.watch, doFetch);
}
const refetch = () => {
if (isClient)
doFetch();
};
const setData = (updater) => {
const currentVal = state.value instanceof AsyncData
? state.value.value.isSome()
? state.value.value.unwrap()
: null
: null;
const newVal = new AsyncData(Some(updater(currentVal)));
if (options.cache !== false)
cache.set(keyStr, newVal);
state.value = newVal;
};
return [state.value, refetch, setData];
}
//# sourceMappingURL=index.js.map