INIT
This commit is contained in:
+139
@@ -0,0 +1,139 @@
|
||||
import { watch } from "../watch/index.js";
|
||||
// Helper functions for debounce and throttle
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function debounce(fn, delay) {
|
||||
let timeoutId;
|
||||
let lastResolve = null;
|
||||
return (...args) => {
|
||||
return new Promise((resolve) => {
|
||||
if (lastResolve) {
|
||||
lastResolve(undefined);
|
||||
}
|
||||
lastResolve = resolve;
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(async () => {
|
||||
const result = await fn(...args);
|
||||
if (lastResolve) {
|
||||
lastResolve(result);
|
||||
lastResolve = null;
|
||||
}
|
||||
}, delay);
|
||||
});
|
||||
};
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function throttle(fn, delay) {
|
||||
let lastRun = 0;
|
||||
let lastPromise = null;
|
||||
return (...args) => {
|
||||
const now = Date.now();
|
||||
if (lastRun && now - lastRun < delay) {
|
||||
return lastPromise ?? Promise.resolve(undefined);
|
||||
}
|
||||
lastRun = now;
|
||||
lastPromise = fn(...args);
|
||||
return lastPromise;
|
||||
};
|
||||
}
|
||||
function runResource(source, fetcher, options = {}, effectFn) {
|
||||
const { lazy = false, once = false, initialValue, debounce: debounceTime, throttle: throttleTime, } = options;
|
||||
// Create state
|
||||
let current = $state(initialValue);
|
||||
let loading = $state(false);
|
||||
let error = $state(undefined);
|
||||
let cleanupFns = $state([]);
|
||||
// Helper function to run cleanup functions
|
||||
const runCleanup = () => {
|
||||
cleanupFns.forEach((fn) => fn());
|
||||
cleanupFns = [];
|
||||
};
|
||||
// Helper function to register cleanup
|
||||
const onCleanup = (fn) => {
|
||||
cleanupFns = [...cleanupFns, fn];
|
||||
};
|
||||
// Create the base fetcher function
|
||||
const baseFetcher = async (value, previousValue, refetching = false) => {
|
||||
try {
|
||||
loading = true;
|
||||
error = undefined;
|
||||
runCleanup();
|
||||
// Create new AbortController for this fetch
|
||||
const controller = new AbortController();
|
||||
onCleanup(() => controller.abort());
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const result = await fetcher(value, previousValue, {
|
||||
data: current,
|
||||
refetching,
|
||||
onCleanup,
|
||||
signal: controller.signal,
|
||||
});
|
||||
current = result;
|
||||
return result;
|
||||
}
|
||||
catch (e) {
|
||||
if (!(e instanceof DOMException && e.name === "AbortError")) {
|
||||
error = e;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
finally {
|
||||
loading = false;
|
||||
}
|
||||
};
|
||||
// Apply debounce or throttle if specified
|
||||
const runFetcher = debounceTime
|
||||
? debounce(baseFetcher, debounceTime)
|
||||
: throttleTime
|
||||
? throttle(baseFetcher, throttleTime)
|
||||
: baseFetcher;
|
||||
// Setup effect
|
||||
const sources = Array.isArray(source) ? source : [source];
|
||||
let prevValues;
|
||||
effectFn((values, previousValues) => {
|
||||
// Skip if once and already ran
|
||||
if (once && prevValues) {
|
||||
return;
|
||||
}
|
||||
prevValues = values;
|
||||
runFetcher(Array.isArray(source) ? values : values[0], Array.isArray(source) ? previousValues : previousValues?.[0]);
|
||||
}, { lazy });
|
||||
return {
|
||||
get current() {
|
||||
return current;
|
||||
},
|
||||
get loading() {
|
||||
return loading;
|
||||
},
|
||||
get error() {
|
||||
return error;
|
||||
},
|
||||
mutate: (value) => {
|
||||
current = value;
|
||||
},
|
||||
refetch: (info) => {
|
||||
const values = sources.map((s) => s());
|
||||
return runFetcher(Array.isArray(source) ? values : values[0], Array.isArray(source) ? values : values[0], info ?? true);
|
||||
},
|
||||
};
|
||||
}
|
||||
// Implementation
|
||||
export function resource(source, fetcher, options) {
|
||||
return runResource(source, fetcher, options, (fn, options) => {
|
||||
const sources = Array.isArray(source) ? source : [source];
|
||||
const getters = () => sources.map((s) => s());
|
||||
watch(getters, (values, previousValues) => {
|
||||
fn(values, previousValues ?? []);
|
||||
}, options);
|
||||
});
|
||||
}
|
||||
// Implementation
|
||||
export function resourcePre(source, fetcher, options) {
|
||||
return runResource(source, fetcher, options, (fn, options) => {
|
||||
const sources = Array.isArray(source) ? source : [source];
|
||||
const getter = () => sources.map((s) => s());
|
||||
watch.pre(getter, (values, previousValues) => {
|
||||
fn(values, previousValues ?? []);
|
||||
}, options);
|
||||
});
|
||||
}
|
||||
resource.pre = resourcePre;
|
||||
Reference in New Issue
Block a user