global: snapshot

This commit is contained in:
nym21
2026-01-12 22:43:56 +01:00
parent b675b70067
commit 5ffb66c0dc
39 changed files with 8207 additions and 11957 deletions

View File

@@ -25,105 +25,106 @@
/** @typedef {MetricResource<unknown>} AnyMetricResource */
/**
* @typedef {ReturnType<typeof createResources>} Resources
* @typedef {{ createResource: typeof createResource, useMetricEndpoint: typeof useMetricEndpoint }} Resources
*/
import signals from "./signals.js";
/**
* @param {Signals} signals
* Create a generic reactive resource wrapper for any async fetcher
* @template T
* @template {any[]} Args
* @param {(...args: Args) => Promise<T>} fetcher
* @returns {Resource<T>}
*/
export function createResources(signals) {
function createResource(fetcher) {
const owner = signals.getOwner();
return signals.runWithOwner(owner, () => {
const data = signals.createSignal(/** @type {T | null} */ (null));
const loading = signals.createSignal(false);
const error = signals.createSignal(/** @type {Error | null} */ (null));
/**
* Create a generic reactive resource wrapper for any async fetcher
* @template T
* @template {any[]} Args
* @param {(...args: Args) => Promise<T>} fetcher
* @returns {Resource<T>}
*/
function createResource(fetcher) {
return signals.runWithOwner(owner, () => {
const data = signals.createSignal(/** @type {T | null} */ (null));
const loading = signals.createSignal(false);
const error = signals.createSignal(/** @type {Error | null} */ (null));
return {
data,
loading,
error,
/**
* @param {Args} args
*/
async fetch(...args) {
loading.set(true);
error.set(null);
try {
const result = await fetcher(...args);
data.set(() => result);
return result;
} catch (e) {
error.set(e instanceof Error ? e : new Error(String(e)));
return null;
} finally {
loading.set(false);
}
},
};
});
}
/**
* Create a reactive resource wrapper for a MetricEndpoint with multi-range support
* @template T
* @param {MetricEndpoint<T>} endpoint
* @returns {MetricResource<T>}
*/
function useMetricEndpoint(endpoint) {
return signals.runWithOwner(owner, () => {
/** @type {Map<string, RangeState<T>>} */
const ranges = new Map();
return {
data,
loading,
error,
/**
* Get or create range state
* @param {number} [from=-10000]
* @param {number} [to]
* @returns {RangeState<T>}
* @param {Args} args
*/
function range(from = -10000, to) {
const key = `${from}-${to ?? ""}`;
const existing = ranges.get(key);
if (existing) return existing;
/** @type {RangeState<T>} */
const state = {
response: signals.createSignal(/** @type {MetricData<T> | null} */ (null)),
loading: signals.createSignal(false),
};
ranges.set(key, state);
return state;
}
return {
path: endpoint.path,
range,
/**
* Fetch data for a range
* @param {number} [from=-10000]
* @param {number} [to]
*/
async fetch(from = -10000, to) {
const r = range(from, to);
r.loading.set(true);
try {
const result = await endpoint.range(from, to, r.response.set);
return result;
} finally {
r.loading.set(false);
}
},
};
});
}
return { createResource, useMetricEndpoint };
async fetch(...args) {
loading.set(true);
error.set(null);
try {
const result = await fetcher(...args);
data.set(() => result);
return result;
} catch (e) {
error.set(e instanceof Error ? e : new Error(String(e)));
return null;
} finally {
loading.set(false);
}
},
};
});
}
/**
* Create a reactive resource wrapper for a MetricEndpoint with multi-range support
* @template T
* @param {MetricEndpoint<T>} endpoint
* @returns {MetricResource<T>}
*/
function useMetricEndpoint(endpoint) {
const owner = signals.getOwner();
return signals.runWithOwner(owner, () => {
/** @type {Map<string, RangeState<T>>} */
const ranges = new Map();
/**
* Get or create range state
* @param {number} [from=-10000]
* @param {number} [to]
* @returns {RangeState<T>}
*/
function range(from = -10000, to) {
const key = `${from}-${to ?? ""}`;
const existing = ranges.get(key);
if (existing) return existing;
/** @type {RangeState<T>} */
const state = {
response: signals.createSignal(
/** @type {MetricData<T> | null} */ (null),
),
loading: signals.createSignal(false),
};
ranges.set(key, state);
return state;
}
return {
path: endpoint.path,
range,
/**
* Fetch data for a range
* @param {number} [start=-10000]
* @param {number} [end]
*/
async fetch(start = -10000, end) {
const r = range(start, end);
r.loading.set(true);
try {
const result = await endpoint
.slice(start, end)
.fetch(r.response.set);
return result;
} finally {
r.loading.set(false);
}
},
};
});
}
export const resources = { createResource, useMetricEndpoint };