mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-10 23:13:33 -07:00
bitview: reorg part 3
This commit is contained in:
@@ -124,13 +124,13 @@ return {
|
||||
contents += " };\n}\n";
|
||||
|
||||
contents += "
|
||||
/** @typedef {ReturnType<typeof createVecIdToIndexes>} VecIdToIndexes
|
||||
/** @typedef {keyof VecIdToIndexes} VecId */
|
||||
/** @typedef {ReturnType<typeof createMetricToIndexes>} MetricToIndexes
|
||||
/** @typedef {keyof MetricToIndexes} Metric */
|
||||
|
||||
/**
|
||||
* @returns {Record<any, number[]>}
|
||||
*/
|
||||
export function createVecIdToIndexes() {
|
||||
export function createMetricToIndexes() {
|
||||
return {
|
||||
";
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { index as serdeIndex } from "./serde";
|
||||
import { runWhenIdle } from "./scheduling";
|
||||
import { serdeIndex } from "./serde";
|
||||
import { runWhenIdle } from "./timing";
|
||||
|
||||
/**
|
||||
* @param {Signals} signals
|
||||
* @param {Utilities} utils
|
||||
* @param {Env} env
|
||||
* @param {VecIdToIndexes} vecIdToIndexes
|
||||
* @param {MetricToIndexes} metricToIndexes
|
||||
*/
|
||||
export function createVecsResources(signals, utils, env, vecIdToIndexes) {
|
||||
export function createVecsResources(signals, utils, env, metricToIndexes) {
|
||||
const owner = signals.getOwner();
|
||||
|
||||
const defaultFrom = -10_000;
|
||||
@@ -31,11 +31,11 @@ export function createVecsResources(signals, utils, env, vecIdToIndexes) {
|
||||
/**
|
||||
* @template {number | OHLCTuple} [T=number]
|
||||
* @param {Index} index
|
||||
* @param {VecId} id
|
||||
* @param {Metric} metric
|
||||
*/
|
||||
function createVecResource(index, id) {
|
||||
if (env.localhost && !(id in vecIdToIndexes)) {
|
||||
throw Error(`${id} not recognized`);
|
||||
function createVecResource(index, metric) {
|
||||
if (env.localhost && !(metric in metricToIndexes)) {
|
||||
throw Error(`${metric} not recognized`);
|
||||
}
|
||||
|
||||
return signals.runWithOwner(owner, () => {
|
||||
@@ -48,7 +48,7 @@ export function createVecsResources(signals, utils, env, vecIdToIndexes) {
|
||||
);
|
||||
|
||||
return {
|
||||
url: api.genUrl(index, id, defaultFrom),
|
||||
url: api.genUrl(index, metric, defaultFrom),
|
||||
fetched: fetchedRecord,
|
||||
/**
|
||||
* Defaults
|
||||
@@ -92,7 +92,7 @@ export function createVecsResources(signals, utils, env, vecIdToIndexes) {
|
||||
}
|
||||
},
|
||||
index,
|
||||
id,
|
||||
metric,
|
||||
from,
|
||||
to,
|
||||
)
|
||||
@@ -112,16 +112,16 @@ export function createVecsResources(signals, utils, env, vecIdToIndexes) {
|
||||
/**
|
||||
* @template {number | OHLCTuple} [T=number]
|
||||
* @param {Index} index
|
||||
* @param {VecId} id
|
||||
* @param {Metric} metric
|
||||
*/
|
||||
getOrCreate(index, id) {
|
||||
const key = `${index},${id}`;
|
||||
getOrCreate(index, metric) {
|
||||
const key = `${index},${metric}`;
|
||||
const found = map.get(key);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
||||
const vec = createVecResource(index, id);
|
||||
const vec = createVecResource(index, metric);
|
||||
if (!vec) throw Error("vec is undefined");
|
||||
map.set(key, /** @type {any} */ (vec));
|
||||
return vec;
|
||||
@@ -230,12 +230,12 @@ async function fetchApi(callback, path, mustBeArray) {
|
||||
|
||||
/**
|
||||
* @param {Index} index
|
||||
* @param {VecId} vecId
|
||||
* @param {Metric} metric
|
||||
* @param {number} [from]
|
||||
* @param {number} [to]
|
||||
*/
|
||||
function genPath(index, vecId, from, to) {
|
||||
let path = `/${serdeIndex.serialize(index)}-to-${vecId.replaceAll("_", "-")}?`;
|
||||
function genPath(index, metric, from, to) {
|
||||
let path = `/${serdeIndex.serialize(index)}-to-${metric.replaceAll("_", "-")}?`;
|
||||
|
||||
if (from !== undefined) {
|
||||
path += `from=${from}`;
|
||||
@@ -252,30 +252,30 @@ function genPath(index, vecId, from, to) {
|
||||
export const api = {
|
||||
/**
|
||||
* @param {Index} index
|
||||
* @param {VecId} vecId
|
||||
* @param {Metric} metric
|
||||
* @param {number} from
|
||||
*/
|
||||
genUrl(index, vecId, from) {
|
||||
return `${API_VECS_PREFIX}${genPath(index, vecId, from)}`;
|
||||
genUrl(index, metric, from) {
|
||||
return `${API_VECS_PREFIX}${genPath(index, metric, from)}`;
|
||||
},
|
||||
/**
|
||||
* @template {number | OHLCTuple} [T=number]
|
||||
* @param {(v: T[]) => void} callback
|
||||
* @param {Index} index
|
||||
* @param {VecId} vecId
|
||||
* @param {Metric} metric
|
||||
* @param {number} [from]
|
||||
* @param {number} [to]
|
||||
*/
|
||||
fetchVec(callback, index, vecId, from, to) {
|
||||
return fetchApi(callback, genPath(index, vecId, from, to), true);
|
||||
fetchVec(callback, index, metric, from, to) {
|
||||
return fetchApi(callback, genPath(index, metric, from, to), true);
|
||||
},
|
||||
/**
|
||||
* @template {number | OHLCTuple} [T=number]
|
||||
* @param {(v: T) => void} callback
|
||||
* @param {Index} index
|
||||
* @param {VecId} vecId
|
||||
* @param {Metric} metric
|
||||
*/
|
||||
fetchLast(callback, index, vecId) {
|
||||
return fetchApi(callback, genPath(index, vecId, -1));
|
||||
fetchLast(callback, index, metric) {
|
||||
return fetchApi(callback, genPath(index, metric, -1));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,6 +15,6 @@ export function range(start, end) {
|
||||
* @template T
|
||||
* @param {T[]} array
|
||||
*/
|
||||
export function random(array) {
|
||||
export function randomFromArray(array) {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
/** @import { IChartApi, ISeriesApi as _ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData as _BaselineData, HistogramData as _HistogramData, SeriesType, IPaneApi, LineSeriesPartialOptions as _LineSeriesPartialOptions, HistogramSeriesPartialOptions as _HistogramSeriesPartialOptions, BaselineSeriesPartialOptions as _BaselineSeriesPartialOptions, CandlestickSeriesPartialOptions as _CandlestickSeriesPartialOptions, WhitespaceData, DeepPartial, ChartOptions, Time, LineData as _LineData } from '../packages/lightweight-charts/5.0.8/dist/typings' */
|
||||
|
||||
import {
|
||||
createChart,
|
||||
CandlestickSeries,
|
||||
HistogramSeries,
|
||||
LineSeries,
|
||||
BaselineSeries,
|
||||
// } from "../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.development.mjs";
|
||||
} from "../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.production.mjs";
|
||||
|
||||
import {
|
||||
createHorizontalChoiceField,
|
||||
createLabeledInput,
|
||||
createSpanName,
|
||||
} from "./dom";
|
||||
import { createOklchToRGBA } from "./colors";
|
||||
import { throttle } from "./scheduling";
|
||||
import { throttle } from "./timing";
|
||||
import { serdeBool } from "./serde";
|
||||
|
||||
/**
|
||||
* @typedef {[number, number, number, number]} OHLCTuple
|
||||
@@ -45,15 +55,6 @@ import { throttle } from "./scheduling";
|
||||
* @typedef {function({ iseries: ISeries; unit: Unit; index: Index }): void} SetDataCallback
|
||||
*/
|
||||
|
||||
import {
|
||||
createChart,
|
||||
CandlestickSeries,
|
||||
HistogramSeries,
|
||||
LineSeries,
|
||||
BaselineSeries,
|
||||
// } from "./5.0.8/dist/lightweight-charts.standalone.development.mjs";
|
||||
} from "../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.production.mjs";
|
||||
|
||||
const oklchToRGBA = createOklchToRGBA();
|
||||
|
||||
const lineWidth = /** @type {any} */ (1.5);
|
||||
@@ -321,7 +322,7 @@ function createChartElement({
|
||||
* @param {number} args.order
|
||||
* @param {Color[]} args.colors
|
||||
* @param {SeriesType} args.seriesType
|
||||
* @param {VecId} [args.vecId]
|
||||
* @param {Metric} [args.metric]
|
||||
* @param {SetDataCallback} [args.setDataCallback]
|
||||
* @param {Accessor<WhitespaceData<number>[]>} [args.data]
|
||||
* @param {number} args.paneIndex
|
||||
@@ -329,7 +330,7 @@ function createChartElement({
|
||||
*/
|
||||
function addSeries({
|
||||
iseries,
|
||||
vecId,
|
||||
metric,
|
||||
name,
|
||||
unit,
|
||||
order,
|
||||
@@ -347,7 +348,7 @@ function createChartElement({
|
||||
save: {
|
||||
keyPrefix: "",
|
||||
key: id,
|
||||
...serde.boolean,
|
||||
...serdeBool,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -382,7 +383,7 @@ function createChartElement({
|
||||
},
|
||||
};
|
||||
|
||||
if (vecId) {
|
||||
if (metric) {
|
||||
signals.createEffect(index, (index) => {
|
||||
const timeResource = vecsResources.getOrCreate(
|
||||
index,
|
||||
@@ -392,7 +393,7 @@ function createChartElement({
|
||||
);
|
||||
timeResource.fetch();
|
||||
|
||||
const valuesResource = vecsResources.getOrCreate(index, vecId);
|
||||
const valuesResource = vecsResources.getOrCreate(index, metric);
|
||||
_valuesResource = valuesResource;
|
||||
|
||||
series.url.set(() => valuesResource.url);
|
||||
@@ -556,7 +557,7 @@ function createChartElement({
|
||||
* @param {string} args.name
|
||||
* @param {Unit} args.unit
|
||||
* @param {number} args.order
|
||||
* @param {VecId} [args.vecId]
|
||||
* @param {Metric} [args.metric]
|
||||
* @param {Accessor<CandlestickData[]>} [args.data]
|
||||
* @param {number} [args.paneIndex]
|
||||
* @param {boolean} [args.defaultActive]
|
||||
@@ -565,7 +566,7 @@ function createChartElement({
|
||||
* @param {CandlestickSeriesPartialOptions} [args.options]
|
||||
*/
|
||||
addCandlestickSeries({
|
||||
vecId,
|
||||
metric,
|
||||
name,
|
||||
unit,
|
||||
order,
|
||||
@@ -607,7 +608,7 @@ function createChartElement({
|
||||
data,
|
||||
setDataCallback,
|
||||
defaultActive,
|
||||
vecId,
|
||||
metric,
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -616,7 +617,7 @@ function createChartElement({
|
||||
* @param {Unit} args.unit
|
||||
* @param {number} args.order
|
||||
* @param {Color} args.color
|
||||
* @param {VecId} [args.vecId]
|
||||
* @param {Metric} [args.metric]
|
||||
* @param {Accessor<HistogramData[]>} [args.data]
|
||||
* @param {number} [args.paneIndex]
|
||||
* @param {boolean} [args.defaultActive]
|
||||
@@ -624,7 +625,7 @@ function createChartElement({
|
||||
* @param {HistogramSeriesPartialOptions} [args.options]
|
||||
*/
|
||||
addHistogramSeries({
|
||||
vecId,
|
||||
metric,
|
||||
name,
|
||||
unit,
|
||||
color,
|
||||
@@ -659,7 +660,7 @@ function createChartElement({
|
||||
data,
|
||||
setDataCallback,
|
||||
defaultActive,
|
||||
vecId,
|
||||
metric,
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -668,7 +669,7 @@ function createChartElement({
|
||||
* @param {Unit} args.unit
|
||||
* @param {number} args.order
|
||||
* @param {Accessor<LineData[]>} [args.data]
|
||||
* @param {VecId} [args.vecId]
|
||||
* @param {Metric} [args.metric]
|
||||
* @param {Color} [args.color]
|
||||
* @param {SetDataCallback} [args.setDataCallback]
|
||||
* @param {number} [args.paneIndex]
|
||||
@@ -676,7 +677,7 @@ function createChartElement({
|
||||
* @param {LineSeriesPartialOptions} [args.options]
|
||||
*/
|
||||
addLineSeries({
|
||||
vecId,
|
||||
metric,
|
||||
name,
|
||||
unit,
|
||||
order,
|
||||
@@ -718,7 +719,7 @@ function createChartElement({
|
||||
setDataCallback,
|
||||
data,
|
||||
defaultActive,
|
||||
vecId,
|
||||
metric,
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -727,14 +728,14 @@ function createChartElement({
|
||||
* @param {Unit} args.unit
|
||||
* @param {number} args.order
|
||||
* @param {Accessor<BaselineData[]>} [args.data]
|
||||
* @param {VecId} [args.vecId]
|
||||
* @param {Metric} [args.metric]
|
||||
* @param {SetDataCallback} [args.setDataCallback]
|
||||
* @param {number} [args.paneIndex]
|
||||
* @param {boolean} [args.defaultActive]
|
||||
* @param {BaselineSeriesPartialOptions} [args.options]
|
||||
*/
|
||||
addBaselineSeries({
|
||||
vecId,
|
||||
metric,
|
||||
name,
|
||||
unit,
|
||||
order,
|
||||
@@ -784,7 +785,7 @@ function createChartElement({
|
||||
unit,
|
||||
data,
|
||||
defaultActive,
|
||||
vecId,
|
||||
metric,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -17,28 +17,21 @@ export function todayUTC() {
|
||||
/**
|
||||
* @param {Date} date
|
||||
*/
|
||||
export function toString(date) {
|
||||
return date.toJSON().split("T")[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Date} date
|
||||
*/
|
||||
export function toDateIndex(date) {
|
||||
export function dateToDateIndex(date) {
|
||||
if (
|
||||
date.getUTCFullYear() === 2009 &&
|
||||
date.getUTCMonth() === 0 &&
|
||||
date.getUTCDate() === 3
|
||||
)
|
||||
return 0;
|
||||
return differenceBetween(date, new Date("2009-01-09"));
|
||||
return differenceBetweenDates(date, new Date("2009-01-09"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Date} start
|
||||
* @param {Date} end
|
||||
*/
|
||||
export function getRange(start, end) {
|
||||
export function createDateRange(start, end) {
|
||||
const dates = /** @type {Date[]} */ ([]);
|
||||
let currentDate = new Date(start);
|
||||
while (currentDate <= end) {
|
||||
@@ -52,17 +45,14 @@ export function getRange(start, end) {
|
||||
* @param {Date} date1
|
||||
* @param {Date} date2
|
||||
*/
|
||||
export function differenceBetween(date1, date2) {
|
||||
export function differenceBetweenDates(date1, date2) {
|
||||
return Math.abs(date1.valueOf() - date2.valueOf()) / ONE_DAY_IN_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Date} oldest
|
||||
* @param {Date} youngest
|
||||
* @returns {number}
|
||||
* @param {Date} date1
|
||||
* @param {Date} date2
|
||||
*/
|
||||
export function getNumberOfDaysBetweenTwoDates(oldest, youngest) {
|
||||
return Math.round(
|
||||
Math.abs((youngest.getTime() - oldest.getTime()) / ONE_DAY_IN_MS),
|
||||
);
|
||||
export function roundedDifferenceBetweenDates(date1, date2) {
|
||||
return Math.round(differenceBetweenDates(date1, date2));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { string as stringSerde } from "./serde";
|
||||
import { serdeString } from "./serde";
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
@@ -253,7 +253,7 @@ export function createHorizontalChoiceField({
|
||||
/** @type {Signal<T[number]>} */
|
||||
const selected = signals.createSignal(defaultValue, {
|
||||
save: {
|
||||
...stringSerde,
|
||||
...serdeString,
|
||||
keyPrefix: keyPrefix ?? "",
|
||||
key,
|
||||
saveDefaultValue: true,
|
||||
|
||||
@@ -4,6 +4,9 @@ import {
|
||||
createAnchorElement,
|
||||
insertElementAtIndex,
|
||||
} from "../dom";
|
||||
import { serdeUnit } from "../serde";
|
||||
import { pushHistory, resetParams } from "../url";
|
||||
import { readStored, writeToStorage } from "../storage";
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
@@ -11,7 +14,7 @@ import {
|
||||
* @param {Signals} args.signals
|
||||
* @param {Env} args.env
|
||||
* @param {Utilities} args.utils
|
||||
* @param {VecIdToIndexes} args.vecIdToIndexes
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Pools} args.pools
|
||||
* @param {Signal<string | null>} args.qrcode
|
||||
*/
|
||||
@@ -21,7 +24,7 @@ export function initOptions({
|
||||
env,
|
||||
utils,
|
||||
qrcode,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
pools,
|
||||
}) {
|
||||
const LS_SELECTED_KEY = `selected_path`;
|
||||
@@ -31,7 +34,7 @@ export function initOptions({
|
||||
.filter((v) => v);
|
||||
const urlPath = urlPath_.length ? urlPath_ : undefined;
|
||||
const savedPath = /** @type {string[]} */ (
|
||||
JSON.parse(storage.read(LS_SELECTED_KEY) || "[]") || []
|
||||
JSON.parse(readStored(LS_SELECTED_KEY) || "[]") || []
|
||||
).filter((v) => v);
|
||||
console.log(savedPath);
|
||||
|
||||
@@ -41,7 +44,7 @@ export function initOptions({
|
||||
const partialOptions = createPartialOptions({
|
||||
env,
|
||||
colors,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
pools,
|
||||
});
|
||||
|
||||
@@ -55,10 +58,10 @@ export function initOptions({
|
||||
*/
|
||||
function arrayToRecord(arr = []) {
|
||||
return (arr || []).reduce((record, blueprint) => {
|
||||
if (env.localhost && !(blueprint.key in vecIdToIndexes)) {
|
||||
throw Error(`${blueprint.key} not recognized`);
|
||||
if (env.localhost && !(blueprint.metric in metricToIndexes)) {
|
||||
throw Error(`${blueprint.metric} not recognized`);
|
||||
}
|
||||
const unit = blueprint.unit ?? utils.vecidToUnit(blueprint.key);
|
||||
const unit = blueprint.unit ?? serdeUnit.deserialize(blueprint.metric);
|
||||
record[unit] ??= [];
|
||||
record[unit].push(blueprint);
|
||||
return record;
|
||||
@@ -69,9 +72,9 @@ export function initOptions({
|
||||
* @param {Option} option
|
||||
*/
|
||||
function selectOption(option) {
|
||||
utils.url.pushHistory(option.path);
|
||||
utils.url.resetParams(option);
|
||||
utils.storage.write(LS_SELECTED_KEY, JSON.stringify(option.path));
|
||||
pushHistory(option.path);
|
||||
resetParams(option);
|
||||
writeToStorage(LS_SELECTED_KEY, JSON.stringify(option.path));
|
||||
selected.set(option);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
*
|
||||
* @typedef {AnySeriesBlueprint["type"]} SeriesType
|
||||
*
|
||||
* @typedef {{ key: VecId, unit?: Unit }} FetchedAnySeriesOptions
|
||||
* @typedef {{ metric: Metric, unit?: Unit }} FetchedAnySeriesOptions
|
||||
*
|
||||
* @typedef {BaselineSeriesBlueprint & FetchedAnySeriesOptions} FetchedBaselineSeriesBlueprint
|
||||
* @typedef {CandlestickSeriesBlueprint & FetchedAnySeriesOptions} FetchedCandlestickSeriesBlueprint
|
||||
@@ -123,18 +123,18 @@
|
||||
* @param {Object} args
|
||||
* @param {Env} args.env
|
||||
* @param {Colors} args.colors
|
||||
* @param {VecIdToIndexes} args.vecIdToIndexes
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Pools} args.pools
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
/**
|
||||
* @template {string} S
|
||||
* @typedef {Extract<VecId, `${S}${string}`>} StartsWith
|
||||
* @typedef {Extract<Metric, `${S}${string}`>} StartsWith
|
||||
*/
|
||||
/**
|
||||
* @template {string} S
|
||||
* @typedef {Extract<VecId, `${string}${S}`>} EndsWith
|
||||
* @typedef {Extract<Metric, `${string}${S}`>} EndsWith
|
||||
*/
|
||||
/**
|
||||
* @template {string} K
|
||||
@@ -155,32 +155,32 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
/**
|
||||
* @typedef {"cumulative_"} CumulativePrefix
|
||||
* @typedef {"_30d_delta"} _30DChageSubString
|
||||
* @typedef {StartsWith<CumulativePrefix>} CumulativeVecId
|
||||
* @typedef {ExcludeSubstring<WithoutPrefix<CumulativeVecId, CumulativePrefix>, _30DChageSubString>} CumulativeVecIdBase
|
||||
* @typedef {StartsWith<CumulativePrefix>} CumulativeMetric
|
||||
* @typedef {ExcludeSubstring<WithoutPrefix<CumulativeMetric, CumulativePrefix>, _30DChageSubString>} CumulativeMetricBase
|
||||
* @typedef {"_avg"} AverageSuffix
|
||||
* @typedef {EndsWith<AverageSuffix>} VecIdAverage
|
||||
* @typedef {WithoutSuffix<VecIdAverage, AverageSuffix>} VecIdAverageBase
|
||||
* @typedef {EndsWith<AverageSuffix>} MetricAverage
|
||||
* @typedef {WithoutSuffix<MetricAverage, AverageSuffix>} MetricAverageBase
|
||||
* @typedef {"_median"} MedianSuffix
|
||||
* @typedef {EndsWith<MedianSuffix>} VecIdMedian
|
||||
* @typedef {WithoutSuffix<VecIdMedian, MedianSuffix>} VecIdMedianBase
|
||||
* @typedef {EndsWith<MedianSuffix>} MetricMedian
|
||||
* @typedef {WithoutSuffix<MetricMedian, MedianSuffix>} MetricMedianBase
|
||||
* @typedef {"_pct90"} _pct90Suffix
|
||||
* @typedef {EndsWith<_pct90Suffix>} VecIdpct90
|
||||
* @typedef {WithoutSuffix<VecIdpct90, _pct90Suffix>} VecIdpct90Base
|
||||
* @typedef {EndsWith<_pct90Suffix>} MetricPct90
|
||||
* @typedef {WithoutSuffix<MetricPct90, _pct90Suffix>} MetricPct90Base
|
||||
* @typedef {"_pct75"} _pct75Suffix
|
||||
* @typedef {EndsWith<_pct75Suffix>} VecIdpct75
|
||||
* @typedef {WithoutSuffix<VecIdpct75, _pct75Suffix>} VecIdpct75Base
|
||||
* @typedef {EndsWith<_pct75Suffix>} MetricPct75
|
||||
* @typedef {WithoutSuffix<MetricPct75, _pct75Suffix>} MetricPct75Base
|
||||
* @typedef {"_pct25"} _pct25Suffix
|
||||
* @typedef {EndsWith<_pct25Suffix>} VecIdpct25
|
||||
* @typedef {WithoutSuffix<VecIdpct25, _pct25Suffix>} VecIdpct25Base
|
||||
* @typedef {EndsWith<_pct25Suffix>} MetricPct25
|
||||
* @typedef {WithoutSuffix<MetricPct25, _pct25Suffix>} MetricPct25Base
|
||||
* @typedef {"_pct10"} _pct10Suffix
|
||||
* @typedef {EndsWith<_pct10Suffix>} VecIdpct10
|
||||
* @typedef {WithoutSuffix<VecIdpct10, _pct10Suffix>} VecIdpct10Base
|
||||
* @typedef {EndsWith<_pct10Suffix>} MetricPct10
|
||||
* @typedef {WithoutSuffix<MetricPct10, _pct10Suffix>} MetricPct10Base
|
||||
* @typedef {"_max"} MaxSuffix
|
||||
* @typedef {EndsWith<MaxSuffix>} VecIdMax
|
||||
* @typedef {WithoutSuffix<VecIdMax, MaxSuffix>} VecIdMaxBase
|
||||
* @typedef {EndsWith<MaxSuffix>} MetricMax
|
||||
* @typedef {WithoutSuffix<MetricMax, MaxSuffix>} MetricMaxBase
|
||||
* @typedef {"_min"} MinSuffix
|
||||
* @typedef {EndsWith<MinSuffix>} VecIdMin
|
||||
* @typedef {WithoutSuffix<VecIdMin, MinSuffix>} VecIdMinBase
|
||||
* @typedef {EndsWith<MinSuffix>} MetricMin
|
||||
* @typedef {WithoutSuffix<MetricMin, MinSuffix>} MetricMinBase
|
||||
*/
|
||||
|
||||
const averages = /** @type {const} */ ([
|
||||
@@ -1135,7 +1135,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {VecId} args.key
|
||||
* @param {Metric} args.key
|
||||
* @param {string} args.name
|
||||
* @param {Color} [args.color]
|
||||
* @param {Unit} [args.unit]
|
||||
@@ -1162,7 +1162,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {VecIdAverageBase} args.concat
|
||||
* @param {MetricAverageBase} args.concat
|
||||
* @param {string} [args.title]
|
||||
*/
|
||||
function createAverageSeries({ concat, title = "" }) {
|
||||
@@ -1174,7 +1174,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {CumulativeVecIdBase} args.concat
|
||||
* @param {CumulativeMetricBase} args.concat
|
||||
* @param {Color} [args.sumColor]
|
||||
* @param {Color} [args.cumulativeColor]
|
||||
* @param {string} [args.common]
|
||||
@@ -1201,14 +1201,14 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {CumulativeVecIdBase} args.key
|
||||
* @param {CumulativeMetricBase} args.key
|
||||
* @param {string} [args.title]
|
||||
* @param {Color} [args.color]
|
||||
*/
|
||||
function createSumSeries({ key, title = "", color }) {
|
||||
const sumKey = `${key}_sum`;
|
||||
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
|
||||
key: `${key}_sum` in vecIdToIndexes ? sumKey : key,
|
||||
key: `${key}_sum` in metricToIndexes ? sumKey : key,
|
||||
title: `Sum ${title}`,
|
||||
color: color ?? colors.red,
|
||||
});
|
||||
@@ -1216,7 +1216,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {CumulativeVecIdBase} args.concat
|
||||
* @param {CumulativeMetricBase} args.concat
|
||||
* @param {string} [args.title]
|
||||
* @param {Color} [args.color]
|
||||
*/
|
||||
@@ -1231,7 +1231,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {VecIdMinBase & VecIdMaxBase & VecIdpct90Base & VecIdpct75Base & VecIdMedianBase & VecIdpct25Base & VecIdpct10Base} args.concat
|
||||
* @param {MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} args.concat
|
||||
* @param {string} [args.title]
|
||||
*/
|
||||
function createMinMaxPercentilesSeries({ concat, title = "" }) {
|
||||
@@ -1282,7 +1282,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {VecIdAverageBase & CumulativeVecIdBase & VecIdMinBase & VecIdMaxBase & VecIdpct90Base & VecIdpct75Base & VecIdMedianBase & VecIdpct25Base & VecIdpct10Base} key
|
||||
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} key
|
||||
*/
|
||||
function createSumCumulativeMinMaxPercentilesSeries(key) {
|
||||
return [
|
||||
@@ -1292,7 +1292,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {VecIdAverageBase & CumulativeVecIdBase & VecIdMinBase & VecIdMaxBase & VecIdpct90Base & VecIdpct75Base & VecIdMedianBase & VecIdpct25Base & VecIdpct10Base} key
|
||||
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} key
|
||||
*/
|
||||
function createAverageSumCumulativeMinMaxPercentilesSeries(key) {
|
||||
return [
|
||||
@@ -1303,7 +1303,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {VecId & VecIdAverageBase & CumulativeVecIdBase & VecIdMinBase & VecIdMaxBase & VecIdpct90Base & VecIdpct75Base & VecIdMedianBase & VecIdpct25Base & VecIdpct10Base} args.key
|
||||
* @param {Metric & MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} args.key
|
||||
* @param {string} args.name
|
||||
*/
|
||||
function createBaseAverageSumCumulativeMinMaxPercentilesSeries({
|
||||
@@ -1321,8 +1321,8 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @typedef {"_ratio_zscore"} RatioZScoreCapSuffix
|
||||
* @typedef {EndsWith<RatioZScoreCapSuffix>} VecIdRatioZScoreCap
|
||||
* @typedef {WithoutSuffix<VecIdRatioZScoreCap, RatioZScoreCapSuffix>} VecIdRatioZScoreCapBase
|
||||
* @typedef {EndsWith<RatioZScoreCapSuffix>} MetricRatioZScoreCap
|
||||
* @typedef {WithoutSuffix<MetricRatioZScoreCap, RatioZScoreCapSuffix>} MetricRatioZScoreCapBase
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1331,7 +1331,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
* @param {string} args.name
|
||||
* @param {string} args.legend
|
||||
* @param {string} args.title
|
||||
* @param {VecIdRatioZScoreCapBase} args.key
|
||||
* @param {MetricRatioZScoreCapBase} args.key
|
||||
* @param {Color} [args.color]
|
||||
*/
|
||||
function createPriceWithRatioOptions({ name, title, legend, key, color }) {
|
||||
@@ -1416,7 +1416,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
name: legend,
|
||||
color,
|
||||
}),
|
||||
...(`${key}_ratio_p1sd_usd` in vecIdToIndexes
|
||||
...(`${key}_ratio_p1sd_usd` in metricToIndexes
|
||||
? percentiles.map(({ name, color }) =>
|
||||
createBaseSeries({
|
||||
key: `${key}_ratio_${name}_usd`,
|
||||
@@ -1439,7 +1439,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
baseValue: { price: 1 },
|
||||
},
|
||||
}),
|
||||
...(`${key}_ratio_p1sd` in vecIdToIndexes
|
||||
...(`${key}_ratio_p1sd` in metricToIndexes
|
||||
? percentiles.map(({ name, color }) =>
|
||||
createBaseSeries({
|
||||
key: `${key}_ratio_${name}`,
|
||||
@@ -1452,7 +1452,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
}),
|
||||
)
|
||||
: []),
|
||||
...(`${key}_ratio_sma` in vecIdToIndexes
|
||||
...(`${key}_ratio_sma` in metricToIndexes
|
||||
? ratioAverages.map(({ name, key: keyAddon, color }) =>
|
||||
createBaseSeries({
|
||||
key: `${key}_ratio_${keyAddon}`,
|
||||
@@ -1471,7 +1471,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
}),
|
||||
],
|
||||
},
|
||||
...(`${key}_ratio_zscore` in vecIdToIndexes
|
||||
...(`${key}_ratio_zscore` in metricToIndexes
|
||||
? [
|
||||
{
|
||||
name: "ZScores",
|
||||
@@ -1665,8 +1665,8 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @typedef {"_supply_in_profit"} SupplyInProfitSuffix
|
||||
* @typedef {EndsWith<SupplyInProfitSuffix>} VecIdSupplyInProfit
|
||||
* @typedef {WithoutSuffix<VecIdSupplyInProfit, SupplyInProfitSuffix>} CohortId
|
||||
* @typedef {EndsWith<SupplyInProfitSuffix>} MetricSupplyInProfit
|
||||
* @typedef {WithoutSuffix<MetricSupplyInProfit, SupplyInProfitSuffix>} CohortId
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -2014,11 +2014,11 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
}),
|
||||
},
|
||||
...(list.filter(
|
||||
({ key }) => `${fixKey(key)}addr_count` in vecIdToIndexes,
|
||||
({ key }) => `${fixKey(key)}addr_count` in metricToIndexes,
|
||||
).length > ("list" in args ? 1 : 0)
|
||||
? !("list" in args) ||
|
||||
list.filter(
|
||||
({ key }) => `${fixKey(key)}empty_addr_count` in vecIdToIndexes,
|
||||
({ key }) => `${fixKey(key)}empty_addr_count` in metricToIndexes,
|
||||
).length <= 1
|
||||
? [
|
||||
{
|
||||
@@ -2027,7 +2027,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
bottom: list.flatMap(({ name, color, key: _key }) => {
|
||||
const key = fixKey(_key);
|
||||
return [
|
||||
...(`${key}addr_count` in vecIdToIndexes
|
||||
...(`${key}addr_count` in metricToIndexes
|
||||
? /** @type {const} */ ([
|
||||
createBaseSeries({
|
||||
key: `${key}addr_count`,
|
||||
@@ -2036,7 +2036,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
}),
|
||||
])
|
||||
: []),
|
||||
...(`${key}empty_addr_count` in vecIdToIndexes
|
||||
...(`${key}empty_addr_count` in metricToIndexes
|
||||
? /** @type {const} */ ([
|
||||
createBaseSeries({
|
||||
key: `${key}empty_addr_count`,
|
||||
@@ -2060,7 +2060,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
bottom: list
|
||||
.filter(
|
||||
({ key }) =>
|
||||
`${fixKey(key)}addr_count` in vecIdToIndexes,
|
||||
`${fixKey(key)}addr_count` in metricToIndexes,
|
||||
)
|
||||
.flatMap(({ name, color, key: _key }) => {
|
||||
const key = fixKey(_key);
|
||||
@@ -2075,7 +2075,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
},
|
||||
...(list.filter(
|
||||
({ key }) =>
|
||||
`${fixKey(key)}empty_addr_count` in vecIdToIndexes,
|
||||
`${fixKey(key)}empty_addr_count` in metricToIndexes,
|
||||
).length
|
||||
? [
|
||||
{
|
||||
@@ -2085,7 +2085,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
.filter(
|
||||
({ key }) =>
|
||||
`${fixKey(key)}empty_addr_count` in
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
)
|
||||
.flatMap(({ name, color, key: _key }) => {
|
||||
const key = fixKey(_key);
|
||||
@@ -2191,7 +2191,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
defaultActive: false,
|
||||
}),
|
||||
...(`${fixKey(args.key)}realized_profit_to_loss_ratio` in
|
||||
vecIdToIndexes
|
||||
metricToIndexes
|
||||
? [
|
||||
createBaseSeries({
|
||||
key: `${fixKey(
|
||||
@@ -2329,7 +2329,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
...(asoprKey in vecIdToIndexes
|
||||
...(asoprKey in metricToIndexes
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -2357,7 +2357,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
...(asoprKey in vecIdToIndexes
|
||||
...(asoprKey in metricToIndexes
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -2385,7 +2385,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
...(asoprKey in vecIdToIndexes
|
||||
...(asoprKey in metricToIndexes
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -2473,7 +2473,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
color,
|
||||
}),
|
||||
...(`${key}realized_profit_to_loss_ratio` in
|
||||
vecIdToIndexes
|
||||
metricToIndexes
|
||||
? [
|
||||
createBaseSeries({
|
||||
key: `${key}realized_profit_to_loss_ratio`,
|
||||
@@ -2634,7 +2634,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
name,
|
||||
key: `${fixKey(key)}adjusted_sopr`,
|
||||
}))
|
||||
.filter(({ key }) => key in vecIdToIndexes);
|
||||
.filter(({ key }) => key in metricToIndexes);
|
||||
|
||||
return reducedList.length
|
||||
? [
|
||||
@@ -2712,7 +2712,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
name: "normal",
|
||||
color: colors.emerald,
|
||||
}),
|
||||
...(adjKey in vecIdToIndexes
|
||||
...(adjKey in metricToIndexes
|
||||
? [
|
||||
createBaseSeries({
|
||||
key: adjKey,
|
||||
@@ -2738,7 +2738,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
name: "normal",
|
||||
color: colors.red,
|
||||
}),
|
||||
...(adjKey in vecIdToIndexes
|
||||
...(adjKey in metricToIndexes
|
||||
? [
|
||||
createBaseSeries({
|
||||
key: adjKey,
|
||||
@@ -2773,7 +2773,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
name,
|
||||
key: `${fixKey(key)}adjusted_value_created`,
|
||||
}))
|
||||
.filter(({ key }) => key in vecIdToIndexes);
|
||||
.filter(({ key }) => key in metricToIndexes);
|
||||
return reducedList.length
|
||||
? [
|
||||
{
|
||||
@@ -2814,7 +2814,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
name,
|
||||
key: `${fixKey(key)}adjusted_value_destroyed`,
|
||||
}))
|
||||
.filter(({ key }) => key in vecIdToIndexes);
|
||||
.filter(({ key }) => key in metricToIndexes);
|
||||
return reducedList.length
|
||||
? [
|
||||
{
|
||||
@@ -2894,7 +2894,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
...(`${fixKey(
|
||||
args.key,
|
||||
)}unrealized_profit_rel_to_own_market_cap` in
|
||||
vecIdToIndexes
|
||||
metricToIndexes
|
||||
? [
|
||||
createBaseSeries({
|
||||
key: `${fixKey(
|
||||
@@ -2930,7 +2930,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
...(`${fixKey(
|
||||
args.key,
|
||||
)}unrealized_profit_rel_to_own_total_unrealized_pnl` in
|
||||
vecIdToIndexes
|
||||
metricToIndexes
|
||||
? [
|
||||
createBaseSeries({
|
||||
key: `${fixKey(
|
||||
@@ -3037,7 +3037,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
}),
|
||||
...(`${fixKey(
|
||||
key,
|
||||
)}net_unrealized_pnl_rel_to_own_market_cap` in vecIdToIndexes
|
||||
)}net_unrealized_pnl_rel_to_own_market_cap` in metricToIndexes
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -3055,7 +3055,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
...(`${fixKey(
|
||||
key,
|
||||
)}net_unrealized_pnl_rel_to_own_total_unrealized_pnl` in
|
||||
vecIdToIndexes
|
||||
metricToIndexes
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -3321,7 +3321,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
|
||||
createPriceLine({
|
||||
unit: "percentage",
|
||||
}),
|
||||
...(`${key}_cagr` in vecIdToIndexes
|
||||
...(`${key}_cagr` in metricToIndexes
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
key: `${key}_cagr`,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const localhost = window.location.hostname === "localhost";
|
||||
|
||||
export const string = {
|
||||
export const serdeString = {
|
||||
/**
|
||||
* @param {string} v
|
||||
*/
|
||||
@@ -15,9 +15,9 @@ export const string = {
|
||||
},
|
||||
};
|
||||
|
||||
export const vecIds = {
|
||||
export const serdeMetrics = {
|
||||
/**
|
||||
* @param {VecId[]} v
|
||||
* @param {Metric[]} v
|
||||
*/
|
||||
serialize(v) {
|
||||
return v.join(",");
|
||||
@@ -26,11 +26,11 @@ export const vecIds = {
|
||||
* @param {string} v
|
||||
*/
|
||||
deserialize(v) {
|
||||
return /** @type {VecId[]} */ (v.split(","));
|
||||
return /** @type {Metric[]} */ (v.split(","));
|
||||
},
|
||||
};
|
||||
|
||||
export const number = {
|
||||
export const serdeNumber = {
|
||||
/**
|
||||
* @param {number} v
|
||||
*/
|
||||
@@ -45,7 +45,7 @@ export const number = {
|
||||
},
|
||||
};
|
||||
|
||||
export const optNumber = {
|
||||
export const serdeOptNumber = {
|
||||
/**
|
||||
* @param {number | null} v
|
||||
*/
|
||||
@@ -60,9 +60,24 @@ export const optNumber = {
|
||||
},
|
||||
};
|
||||
|
||||
export const optDate = {
|
||||
export const serdeDate = {
|
||||
/**
|
||||
* @param {Date | null} date
|
||||
* @param {Date} date
|
||||
*/
|
||||
serialize(date) {
|
||||
return date.toString();
|
||||
},
|
||||
/**
|
||||
* @param {string} v
|
||||
*/
|
||||
deserialize(v) {
|
||||
return new Date(v);
|
||||
},
|
||||
};
|
||||
|
||||
export const serdeOptDate = {
|
||||
/**
|
||||
* @param {Date | null} date
|
||||
*/
|
||||
serialize(date) {
|
||||
return date !== null ? date.toString() : "";
|
||||
@@ -75,7 +90,7 @@ export const optDate = {
|
||||
},
|
||||
};
|
||||
|
||||
export const boolean = {
|
||||
export const serdeBool = {
|
||||
/**
|
||||
* @param {boolean} v
|
||||
*/
|
||||
@@ -96,7 +111,7 @@ export const boolean = {
|
||||
},
|
||||
};
|
||||
|
||||
export const index = {
|
||||
export const serdeIndex = {
|
||||
/**
|
||||
* @param {Index} v
|
||||
*/
|
||||
@@ -160,7 +175,7 @@ export const index = {
|
||||
},
|
||||
};
|
||||
|
||||
export const chartableIndex = {
|
||||
export const serdeChartableIndex = {
|
||||
/**
|
||||
* @param {number} v
|
||||
* @returns {SerializedChartableIndex | null}
|
||||
@@ -278,9 +293,9 @@ export const chartableIndex = {
|
||||
* "" } Unit
|
||||
*/
|
||||
|
||||
export const unit = {
|
||||
export const serdeUnit = {
|
||||
/**
|
||||
* @param {VecId} v
|
||||
* @param {string} v
|
||||
*/
|
||||
deserialize(v) {
|
||||
/** @type {Unit | undefined} */
|
||||
|
||||
@@ -1,49 +1,51 @@
|
||||
export default {
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
readNumber(key) {
|
||||
const saved = this.read(key);
|
||||
if (saved) {
|
||||
return Number(saved);
|
||||
}
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
export function readStoredNumber(key) {
|
||||
const saved = readStored(key);
|
||||
if (saved) {
|
||||
return Number(saved);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
export function readStoredBool(key) {
|
||||
const saved = readStored(key);
|
||||
if (saved) {
|
||||
return saved === "true" || saved === "1";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
export function readStored(key) {
|
||||
try {
|
||||
return localStorage.getItem(key);
|
||||
} catch (_) {
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
readBool(key) {
|
||||
const saved = this.read(key);
|
||||
if (saved) {
|
||||
return saved === "true" || saved === "1";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
read(key) {
|
||||
try {
|
||||
return localStorage.getItem(key);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string | boolean | null | undefined} value
|
||||
*/
|
||||
write(key, value) {
|
||||
try {
|
||||
value !== undefined && value !== null
|
||||
? localStorage.setItem(key, String(value))
|
||||
: localStorage.removeItem(key);
|
||||
} catch (_) {}
|
||||
},
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
remove(key) {
|
||||
this.write(key, undefined);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string | boolean | null | undefined} value
|
||||
*/
|
||||
export function writeToStorage(key, value) {
|
||||
try {
|
||||
value !== undefined && value !== null
|
||||
? localStorage.setItem(key, String(value))
|
||||
: localStorage.removeItem(key);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
export function removeStored(key) {
|
||||
writeToStorage(key, undefined);
|
||||
}
|
||||
|
||||
@@ -6,96 +6,102 @@ function processPathname(pathname) {
|
||||
return Array.isArray(pathname) ? pathname.join("/") : pathname;
|
||||
}
|
||||
|
||||
export default {
|
||||
chartParamsWhitelist: ["from", "to"],
|
||||
/**
|
||||
* @param {string | string[]} pathname
|
||||
*/
|
||||
pushHistory(pathname) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
pathname = processPathname(pathname);
|
||||
try {
|
||||
const url = `/${pathname}?${urlParams.toString()}`;
|
||||
console.log(`push history: ${url}`);
|
||||
window.history.pushState(null, "", url);
|
||||
} catch (_) {}
|
||||
},
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {URLSearchParams} [args.urlParams]
|
||||
* @param {string | string[]} [args.pathname]
|
||||
*/
|
||||
replaceHistory({ urlParams, pathname }) {
|
||||
urlParams ||= new URLSearchParams(window.location.search);
|
||||
pathname = processPathname(pathname);
|
||||
try {
|
||||
const url = `/${pathname}?${urlParams.toString()}`;
|
||||
console.log(`replace history: ${url}`);
|
||||
window.history.replaceState(null, "", url);
|
||||
} catch (_) {}
|
||||
},
|
||||
/**
|
||||
* @param {Option} option
|
||||
*/
|
||||
resetParams(option) {
|
||||
const urlParams = new URLSearchParams();
|
||||
if (option.kind === "chart") {
|
||||
[...new URLSearchParams(window.location.search).entries()]
|
||||
.filter(([key, _]) => this.chartParamsWhitelist.includes(key))
|
||||
.forEach(([key, value]) => {
|
||||
urlParams.set(key, value);
|
||||
});
|
||||
}
|
||||
this.replaceHistory({ urlParams, pathname: option.path.join("/") });
|
||||
},
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string | boolean | null | undefined} value
|
||||
*/
|
||||
writeParam(key, value) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const chartParamsWhitelist = ["from", "to"];
|
||||
|
||||
if (value !== null && value !== undefined) {
|
||||
urlParams.set(key, String(value));
|
||||
} else {
|
||||
urlParams.delete(key);
|
||||
}
|
||||
/**
|
||||
* @param {string | string[]} pathname
|
||||
*/
|
||||
export function pushHistory(pathname) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
pathname = processPathname(pathname);
|
||||
try {
|
||||
const url = `/${pathname}?${urlParams.toString()}`;
|
||||
console.log(`push history: ${url}`);
|
||||
window.history.pushState(null, "", url);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
this.replaceHistory({ urlParams });
|
||||
},
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
removeParam(key) {
|
||||
this.writeParam(key, undefined);
|
||||
},
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
readBoolParam(key) {
|
||||
const param = this.readParam(key);
|
||||
if (param) {
|
||||
return param === "true" || param === "1";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
readNumberParam(key) {
|
||||
const param = this.readParam(key);
|
||||
if (param) {
|
||||
return Number(param);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} key
|
||||
* @returns {string | null}
|
||||
*/
|
||||
readParam(key) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get(key);
|
||||
},
|
||||
};
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {URLSearchParams} [args.urlParams]
|
||||
* @param {string | string[]} [args.pathname]
|
||||
*/
|
||||
export function replaceHistory({ urlParams, pathname }) {
|
||||
urlParams ||= new URLSearchParams(window.location.search);
|
||||
pathname = processPathname(pathname);
|
||||
try {
|
||||
const url = `/${pathname}?${urlParams.toString()}`;
|
||||
console.log(`replace history: ${url}`);
|
||||
window.history.replaceState(null, "", url);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Option} option
|
||||
*/
|
||||
export function resetParams(option) {
|
||||
const urlParams = new URLSearchParams();
|
||||
if (option.kind === "chart") {
|
||||
[...new URLSearchParams(window.location.search).entries()]
|
||||
.filter(([key, _]) => chartParamsWhitelist.includes(key))
|
||||
.forEach(([key, value]) => {
|
||||
urlParams.set(key, value);
|
||||
});
|
||||
}
|
||||
replaceHistory({ urlParams, pathname: option.path.join("/") });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string | boolean | null | undefined} value
|
||||
*/
|
||||
export function writeParam(key, value) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
if (value !== null && value !== undefined) {
|
||||
urlParams.set(key, String(value));
|
||||
} else {
|
||||
urlParams.delete(key);
|
||||
}
|
||||
|
||||
replaceHistory({ urlParams });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
export function removeParam(key) {
|
||||
writeParam(key, undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
export function readBoolParam(key) {
|
||||
const param = readParam(key);
|
||||
if (param) {
|
||||
return param === "true" || param === "1";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
export function readNumberParam(key) {
|
||||
const param = readParam(key);
|
||||
if (param) {
|
||||
return Number(param);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} key
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export function readParam(key) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get(key);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
* @import { Signal, Signals, Accessor } from "./packages/solidjs-signals/wrapper";
|
||||
*
|
||||
* @import { DateIndex, DecadeIndex, DifficultyEpoch, Index, HalvingEpoch, Height, MonthIndex, P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2MSOutputIndex, P2AAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, TxIndex, InputIndex, OutputIndex, VecId, WeekIndex, SemesterIndex, YearIndex, VecIdToIndexes, QuarterIndex, EmptyOutputIndex, OpReturnIndex, UnknownOutputIndex, EmptyAddressIndex, LoadedAddressIndex } from "./bridge/vecs"
|
||||
* @import { DateIndex, DecadeIndex, DifficultyEpoch, Index, HalvingEpoch, Height, MonthIndex, P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2MSOutputIndex, P2AAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, TxIndex, InputIndex, OutputIndex, WeekIndex, SemesterIndex, YearIndex, MetricToIndexes, QuarterIndex, EmptyOutputIndex, OpReturnIndex, UnknownOutputIndex, EmptyAddressIndex, LoadedAddressIndex } from "./bridge/vecs"
|
||||
*
|
||||
* @import { Pools, Pool } from "./bridge/pools"
|
||||
*
|
||||
@@ -26,6 +26,7 @@
|
||||
* @typedef {typeof import("./core/utils")} Utilities
|
||||
* @typedef {typeof import("./core/env")["default"]} Env
|
||||
* @typedef {typeof import("./core/elements")["default"]} Elements
|
||||
* @typedef {string} Metric
|
||||
*/
|
||||
|
||||
// DO NOT CHANGE, Exact format is expected in `brk_bundler`
|
||||
|
||||
@@ -4,7 +4,11 @@ import * as utils from "./core/utils";
|
||||
import elements from "./core/elements";
|
||||
import env from "./core/env";
|
||||
import packages from "./lazy";
|
||||
import { onFirstIntersection, getElementById } from "./core/dom";
|
||||
import { onFirstIntersection, getElementById, isHidden } from "./core/dom";
|
||||
import { next } from "./core/timing";
|
||||
import { replaceHistory } from "./core/url";
|
||||
import { serdeUnit } from "./core/serde";
|
||||
import { removeStored, writeToStorage } from "./core/storage";
|
||||
|
||||
function initFrameSelectors() {
|
||||
const children = Array.from(elements.selectors.children);
|
||||
@@ -95,7 +99,7 @@ Promise.all([
|
||||
]).then(
|
||||
([
|
||||
signals,
|
||||
{ createVecIdToIndexes, VERSION },
|
||||
{ createMetricToIndexes, VERSION },
|
||||
{ createPools },
|
||||
{ initOptions },
|
||||
]) =>
|
||||
@@ -104,11 +108,11 @@ Promise.all([
|
||||
|
||||
console.log(`VERSION = ${VERSION}`);
|
||||
|
||||
const vecIdToIndexes = createVecIdToIndexes();
|
||||
const metricToIndexes = createMetricToIndexes();
|
||||
|
||||
if (env.localhost) {
|
||||
Object.keys(vecIdToIndexes).forEach((id) => {
|
||||
utils.vecidToUnit(/** @type {VecId} */ (id));
|
||||
Object.keys(metricToIndexes).forEach((metric) => {
|
||||
serdeUnit.deserialize(metric);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -154,7 +158,7 @@ Promise.all([
|
||||
signals,
|
||||
utils,
|
||||
env,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
);
|
||||
|
||||
const pools = createPools();
|
||||
@@ -167,7 +171,7 @@ Promise.all([
|
||||
signals,
|
||||
utils,
|
||||
qrcode,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
pools,
|
||||
});
|
||||
|
||||
@@ -242,7 +246,7 @@ Promise.all([
|
||||
utils,
|
||||
webSockets,
|
||||
vecsResources,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -275,7 +279,7 @@ Promise.all([
|
||||
utils,
|
||||
webSockets,
|
||||
vecsResources,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
packages,
|
||||
}),
|
||||
),
|
||||
@@ -298,7 +302,7 @@ Promise.all([
|
||||
utils,
|
||||
vecsResources,
|
||||
option,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -344,7 +348,7 @@ Promise.all([
|
||||
}
|
||||
|
||||
if (!previousElement) {
|
||||
utils.url.replaceHistory({ pathname: option.path });
|
||||
replaceHistory({ pathname: option.path });
|
||||
}
|
||||
|
||||
previousElement = element;
|
||||
@@ -354,7 +358,7 @@ Promise.all([
|
||||
function createMobileSwitchEffect() {
|
||||
let firstRun = true;
|
||||
signals.createEffect(options.selected, () => {
|
||||
if (!firstRun && !utils.dom.isHidden(elements.asideLabel)) {
|
||||
if (!firstRun && !isHidden(elements.asideLabel)) {
|
||||
elements.asideLabel.click();
|
||||
}
|
||||
firstRun = false;
|
||||
@@ -382,12 +386,12 @@ Promise.all([
|
||||
ul = /** @type {HTMLUListElement} */ (
|
||||
elements.nav.firstElementChild
|
||||
);
|
||||
await utils.next();
|
||||
await next();
|
||||
if (!ul) {
|
||||
await getFirstChild();
|
||||
}
|
||||
} catch (_) {
|
||||
await utils.next();
|
||||
await next();
|
||||
await getFirstChild();
|
||||
}
|
||||
}
|
||||
@@ -405,7 +409,7 @@ Promise.all([
|
||||
ul.querySelectorAll(":scope > li > details"),
|
||||
);
|
||||
if (!detailsList.length) {
|
||||
await utils.next();
|
||||
await next();
|
||||
}
|
||||
}
|
||||
const details = detailsList.find((s) => s.dataset.name == name);
|
||||
@@ -417,7 +421,7 @@ Promise.all([
|
||||
Array.from(details.querySelectorAll(":scope > ul"))
|
||||
);
|
||||
if (!uls.length) {
|
||||
await utils.next();
|
||||
await next();
|
||||
} else if (uls.length > 1) {
|
||||
throw "Shouldn't be possible";
|
||||
} else {
|
||||
@@ -430,7 +434,7 @@ Promise.all([
|
||||
while (!anchors.length) {
|
||||
anchors = Array.from(ul.querySelectorAll(":scope > li > a"));
|
||||
if (!anchors.length) {
|
||||
await utils.next();
|
||||
await next();
|
||||
}
|
||||
}
|
||||
anchors
|
||||
@@ -684,12 +688,12 @@ Promise.all([
|
||||
try {
|
||||
if (typeof width === "number") {
|
||||
elements.main.style.width = `${width}px`;
|
||||
utils.storage.write(barWidthLocalStorageKey, String(width));
|
||||
writeToStorage(barWidthLocalStorageKey, String(width));
|
||||
} else {
|
||||
elements.main.style.width = elements.style.getPropertyValue(
|
||||
"--default-main-width",
|
||||
);
|
||||
utils.storage.remove(barWidthLocalStorageKey);
|
||||
removeStored(barWidthLocalStorageKey);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ import {
|
||||
createHorizontalChoiceField,
|
||||
createHeader,
|
||||
} from "../core/dom";
|
||||
import { throttle } from "../core/scheduling";
|
||||
import { serdeChartableIndex, serdeOptNumber } from "../core/serde";
|
||||
import { throttle } from "../core/timing";
|
||||
|
||||
const keyPrefix = "chart";
|
||||
const ONE_BTC_IN_SATS = 100_000_000;
|
||||
@@ -26,7 +27,7 @@ const CANDLE = "candle";
|
||||
* @param {Elements} args.elements
|
||||
* @param {Env} args.env
|
||||
* @param {VecsResources} args.vecsResources
|
||||
* @param {VecIdToIndexes} args.vecIdToIndexes
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Packages} args.packages
|
||||
*/
|
||||
export function init({
|
||||
@@ -39,7 +40,7 @@ export function init({
|
||||
env,
|
||||
webSockets,
|
||||
vecsResources,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
packages,
|
||||
}) {
|
||||
elements.charts.append(createShadow("left"));
|
||||
@@ -50,7 +51,7 @@ export function init({
|
||||
|
||||
const { index, fieldset } = createIndexSelector({
|
||||
option,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
signals,
|
||||
utils,
|
||||
});
|
||||
@@ -63,7 +64,7 @@ export function init({
|
||||
|
||||
const from = signals.createSignal(/** @type {number | null} */ (null), {
|
||||
save: {
|
||||
...utils.serde.optNumber,
|
||||
...serdeOptNumber,
|
||||
keyPrefix: TIMERANGE_LS_KEY,
|
||||
key: "from",
|
||||
serializeParam: firstRun,
|
||||
@@ -71,7 +72,7 @@ export function init({
|
||||
});
|
||||
const to = signals.createSignal(/** @type {number | null} */ (null), {
|
||||
save: {
|
||||
...utils.serde.optNumber,
|
||||
...serdeOptNumber,
|
||||
keyPrefix: TIMERANGE_LS_KEY,
|
||||
key: "to",
|
||||
serializeParam: firstRun,
|
||||
@@ -340,7 +341,7 @@ export function init({
|
||||
case null:
|
||||
case CANDLE: {
|
||||
series = chart.addCandlestickSeries({
|
||||
vecId: "price_ohlc",
|
||||
metric: "price_ohlc",
|
||||
name: "Price",
|
||||
unit: topUnit,
|
||||
setDataCallback: printLatest,
|
||||
@@ -350,7 +351,7 @@ export function init({
|
||||
}
|
||||
case LINE: {
|
||||
series = chart.addLineSeries({
|
||||
vecId: "price_close",
|
||||
metric: "price_close",
|
||||
name: "Price",
|
||||
unit: topUnit,
|
||||
color: colors.default,
|
||||
@@ -370,7 +371,7 @@ export function init({
|
||||
case null:
|
||||
case CANDLE: {
|
||||
series = chart.addCandlestickSeries({
|
||||
vecId: "price_ohlc_in_sats",
|
||||
metric: "price_ohlc_in_sats",
|
||||
name: "Price",
|
||||
unit: topUnit,
|
||||
inverse: true,
|
||||
@@ -381,7 +382,7 @@ export function init({
|
||||
}
|
||||
case LINE: {
|
||||
series = chart.addLineSeries({
|
||||
vecId: "price_close_in_sats",
|
||||
metric: "price_close_in_sats",
|
||||
name: "Price",
|
||||
unit: topUnit,
|
||||
color: colors.default,
|
||||
@@ -446,7 +447,7 @@ export function init({
|
||||
order += orderStart;
|
||||
|
||||
const indexes = /** @type {readonly number[]} */ (
|
||||
vecIdToIndexes[blueprint.key]
|
||||
metricToIndexes[blueprint.metric]
|
||||
);
|
||||
|
||||
if (indexes.includes(index)) {
|
||||
@@ -454,7 +455,7 @@ export function init({
|
||||
case "Baseline": {
|
||||
seriesList.push(
|
||||
chart.addBaselineSeries({
|
||||
vecId: blueprint.key,
|
||||
metric: blueprint.metric,
|
||||
name: blueprint.title,
|
||||
unit,
|
||||
defaultActive: blueprint.defaultActive,
|
||||
@@ -474,7 +475,7 @@ export function init({
|
||||
case "Histogram": {
|
||||
seriesList.push(
|
||||
chart.addHistogramSeries({
|
||||
vecId: blueprint.key,
|
||||
metric: blueprint.metric,
|
||||
name: blueprint.title,
|
||||
unit,
|
||||
color: blueprint.color,
|
||||
@@ -493,7 +494,7 @@ export function init({
|
||||
case undefined:
|
||||
seriesList.push(
|
||||
chart.addLineSeries({
|
||||
vecId: blueprint.key,
|
||||
metric: blueprint.metric,
|
||||
color: blueprint.color,
|
||||
name: blueprint.title,
|
||||
unit,
|
||||
@@ -518,11 +519,11 @@ export function init({
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Accessor<ChartOption>} args.option
|
||||
* @param {VecIdToIndexes} args.vecIdToIndexes
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
*/
|
||||
function createIndexSelector({ option, vecIdToIndexes, signals, utils }) {
|
||||
function createIndexSelector({ option, metricToIndexes, signals, utils }) {
|
||||
const choices_ = /** @satisfies {SerializedChartableIndex[]} */ ([
|
||||
"timestamp",
|
||||
"date",
|
||||
@@ -546,13 +547,13 @@ function createIndexSelector({ option, vecIdToIndexes, signals, utils }) {
|
||||
const rawIndexes = new Set(
|
||||
[Object.values(o.top), Object.values(o.bottom)]
|
||||
.flat(2)
|
||||
.filter((blueprint) => !blueprint.key.startsWith("constant_"))
|
||||
.map((blueprint) => vecIdToIndexes[blueprint.key])
|
||||
.filter((blueprint) => !blueprint.metric.startsWith("constant_"))
|
||||
.map((blueprint) => metricToIndexes[blueprint.metric])
|
||||
.flat(),
|
||||
);
|
||||
|
||||
const serializedIndexes = [...rawIndexes].flatMap((index) => {
|
||||
const c = utils.serde.chartableIndex.serialize(index);
|
||||
const c = serdeChartableIndex.serialize(index);
|
||||
return c ? [c] : [];
|
||||
});
|
||||
|
||||
@@ -581,7 +582,7 @@ function createIndexSelector({ option, vecIdToIndexes, signals, utils }) {
|
||||
fieldset.dataset.size = "sm";
|
||||
|
||||
const index = signals.createMemo(() =>
|
||||
utils.serde.chartableIndex.deserialize(selected()),
|
||||
serdeChartableIndex.deserialize(selected()),
|
||||
);
|
||||
|
||||
return { fieldset, index };
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { randomFromArray } from "../core/array";
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
@@ -9,7 +10,7 @@
|
||||
* @param {WebSockets} args.webSockets
|
||||
* @param {Elements} args.elements
|
||||
* @param {VecsResources} args.vecsResources
|
||||
* @param {VecIdToIndexes} args.vecIdToIndexes
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
*/
|
||||
export function init({
|
||||
colors,
|
||||
@@ -20,7 +21,7 @@ export function init({
|
||||
utils,
|
||||
webSockets,
|
||||
vecsResources,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
}) {
|
||||
const chain = window.document.createElement("div");
|
||||
chain.id = "chain";
|
||||
@@ -41,7 +42,7 @@ export function init({
|
||||
];
|
||||
|
||||
for (let i = 0; i <= 10; i++) {
|
||||
const { name, color } = utils.array.random(miners);
|
||||
const { name, color } = randomFromArray(miners);
|
||||
const { cubeElement, leftFaceElement, rightFaceElement, topFaceElement } =
|
||||
createCube();
|
||||
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
import {
|
||||
createDateRange,
|
||||
dateToDateIndex,
|
||||
differenceBetweenDates,
|
||||
} from "../core/date";
|
||||
import {
|
||||
createButtonElement,
|
||||
createFieldElement,
|
||||
createHeader,
|
||||
createSelect,
|
||||
} from "../core/dom";
|
||||
import { serdeDate, serdeOptDate, serdeOptNumber } from "../core/serde";
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
@@ -5,8 +17,6 @@
|
||||
* @param {CreateChartElement} args.createChartElement
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Serde} args.serde
|
||||
* @param {Dom} args.dom
|
||||
* @param {Elements} args.elements
|
||||
* @param {VecsResources} args.vecsResources
|
||||
*/
|
||||
@@ -15,8 +25,6 @@ export function init({
|
||||
elements,
|
||||
createChartElement,
|
||||
signals,
|
||||
serde,
|
||||
dom,
|
||||
utils,
|
||||
vecsResources,
|
||||
}) {
|
||||
@@ -126,7 +134,7 @@ export function init({
|
||||
const min = "2011-01-01";
|
||||
const minDate = new Date(min);
|
||||
const maxDate = new Date();
|
||||
const max = utils.date.toString(maxDate);
|
||||
const max = serdeDate.serialize(maxDate);
|
||||
input.min = min;
|
||||
input.max = max;
|
||||
|
||||
@@ -135,7 +143,7 @@ export function init({
|
||||
signals.createEffect(
|
||||
() => {
|
||||
const dateSignal = signal();
|
||||
return dateSignal ? utils.date.toString(dateSignal) : "";
|
||||
return dateSignal ? serdeDate.serialize(dateSignal) : "";
|
||||
},
|
||||
(value) => {
|
||||
if (stateValue !== value) {
|
||||
@@ -169,7 +177,7 @@ export function init({
|
||||
if (!element) throw "createResetableField element missing";
|
||||
div.append(element);
|
||||
|
||||
const button = dom.createButtonElement({
|
||||
const button = createButtonElement({
|
||||
onClick: signal.reset,
|
||||
inside: "Reset",
|
||||
title: "Reset field",
|
||||
@@ -309,7 +317,7 @@ export function init({
|
||||
initial: {
|
||||
amount: signals.createSignal(/** @type {number | null} */ (1000), {
|
||||
save: {
|
||||
...serde.optNumber,
|
||||
...serdeOptNumber,
|
||||
keyPrefix,
|
||||
key: "initial-amount",
|
||||
},
|
||||
@@ -318,7 +326,7 @@ export function init({
|
||||
topUp: {
|
||||
amount: signals.createSignal(/** @type {number | null} */ (150), {
|
||||
save: {
|
||||
...serde.optNumber,
|
||||
...serdeOptNumber,
|
||||
keyPrefix,
|
||||
key: "top-up-amount",
|
||||
},
|
||||
@@ -339,14 +347,14 @@ export function init({
|
||||
investment: {
|
||||
initial: signals.createSignal(/** @type {number | null} */ (1000), {
|
||||
save: {
|
||||
...serde.optNumber,
|
||||
...serdeOptNumber,
|
||||
keyPrefix,
|
||||
key: "initial-swap",
|
||||
},
|
||||
}),
|
||||
recurrent: signals.createSignal(/** @type {number | null} */ (5), {
|
||||
save: {
|
||||
...serde.optNumber,
|
||||
...serdeOptNumber,
|
||||
keyPrefix,
|
||||
key: "recurrent-swap",
|
||||
},
|
||||
@@ -368,7 +376,7 @@ export function init({
|
||||
/** @type {Date | null} */ (new Date("2021-04-15")),
|
||||
{
|
||||
save: {
|
||||
...serde.optDate,
|
||||
...serdeOptDate,
|
||||
keyPrefix,
|
||||
key: "interval-start",
|
||||
},
|
||||
@@ -376,7 +384,7 @@ export function init({
|
||||
),
|
||||
end: signals.createSignal(/** @type {Date | null} */ (new Date()), {
|
||||
save: {
|
||||
...serde.optDate,
|
||||
...serdeOptDate,
|
||||
keyPrefix,
|
||||
key: "interval-end",
|
||||
},
|
||||
@@ -385,7 +393,7 @@ export function init({
|
||||
fees: {
|
||||
percentage: signals.createSignal(/** @type {number | null} */ (0.25), {
|
||||
save: {
|
||||
...serde.optNumber,
|
||||
...serdeOptNumber,
|
||||
keyPrefix,
|
||||
key: "percentage",
|
||||
},
|
||||
@@ -393,7 +401,7 @@ export function init({
|
||||
},
|
||||
};
|
||||
|
||||
parametersElement.append(dom.createHeader("Save in Bitcoin").headerElement);
|
||||
parametersElement.append(createHeader("Save in Bitcoin").headerElement);
|
||||
|
||||
/**
|
||||
* @param {Object} param0
|
||||
@@ -418,7 +426,7 @@ export function init({
|
||||
}
|
||||
|
||||
parametersElement.append(
|
||||
dom.createFieldElement({
|
||||
createFieldElement({
|
||||
title: createColoredTypeHTML({
|
||||
color: "green",
|
||||
type: "Dollars",
|
||||
@@ -438,7 +446,7 @@ export function init({
|
||||
);
|
||||
|
||||
parametersElement.append(
|
||||
dom.createFieldElement({
|
||||
createFieldElement({
|
||||
title: createColoredTypeHTML({
|
||||
color: "green",
|
||||
type: "Dollars",
|
||||
@@ -447,7 +455,7 @@ export function init({
|
||||
description:
|
||||
"The frequency at which you'll top up your account at the exchange.",
|
||||
input: domExtended.createResetableInput(
|
||||
dom.createSelect({
|
||||
createSelect({
|
||||
id: "top-up-frequency",
|
||||
list: frequencies.list,
|
||||
signal: settings.dollars.topUp.frenquency,
|
||||
@@ -458,7 +466,7 @@ export function init({
|
||||
);
|
||||
|
||||
parametersElement.append(
|
||||
dom.createFieldElement({
|
||||
createFieldElement({
|
||||
title: createColoredTypeHTML({
|
||||
color: "green",
|
||||
type: "Dollars",
|
||||
@@ -478,7 +486,7 @@ export function init({
|
||||
);
|
||||
|
||||
parametersElement.append(
|
||||
dom.createFieldElement({
|
||||
createFieldElement({
|
||||
title: createColoredTypeHTML({
|
||||
color: "orange",
|
||||
type: "Bitcoin",
|
||||
@@ -498,7 +506,7 @@ export function init({
|
||||
);
|
||||
|
||||
parametersElement.append(
|
||||
dom.createFieldElement({
|
||||
createFieldElement({
|
||||
title: createColoredTypeHTML({
|
||||
color: "orange",
|
||||
type: "Bitcoin",
|
||||
@@ -506,7 +514,7 @@ export function init({
|
||||
}),
|
||||
description: "The frequency at which you'll be buying Bitcoin.",
|
||||
input: domExtended.createResetableInput(
|
||||
dom.createSelect({
|
||||
createSelect({
|
||||
id: "investment-frequency",
|
||||
list: frequencies.list,
|
||||
signal: settings.bitcoin.investment.frequency,
|
||||
@@ -517,7 +525,7 @@ export function init({
|
||||
);
|
||||
|
||||
parametersElement.append(
|
||||
dom.createFieldElement({
|
||||
createFieldElement({
|
||||
title: createColoredTypeHTML({
|
||||
color: "orange",
|
||||
type: "Bitcoin",
|
||||
@@ -537,7 +545,7 @@ export function init({
|
||||
);
|
||||
|
||||
parametersElement.append(
|
||||
dom.createFieldElement({
|
||||
createFieldElement({
|
||||
title: createColoredTypeHTML({
|
||||
color: "sky",
|
||||
type: "Interval",
|
||||
@@ -556,7 +564,7 @@ export function init({
|
||||
);
|
||||
|
||||
parametersElement.append(
|
||||
dom.createFieldElement({
|
||||
createFieldElement({
|
||||
title: createColoredTypeHTML({
|
||||
color: "sky",
|
||||
type: "Interval",
|
||||
@@ -575,7 +583,7 @@ export function init({
|
||||
);
|
||||
|
||||
parametersElement.append(
|
||||
dom.createFieldElement({
|
||||
createFieldElement({
|
||||
title: createColoredTypeHTML({
|
||||
color: "red",
|
||||
type: "Fees",
|
||||
@@ -869,7 +877,7 @@ export function init({
|
||||
}) => {
|
||||
if (!start || !end || start > end) return;
|
||||
|
||||
const range = utils.date.getRange(start, end);
|
||||
const range = createDateRange(start, end);
|
||||
|
||||
totalInvestedAmountData().length = 0;
|
||||
bitcoinValueData().length = 0;
|
||||
@@ -916,7 +924,7 @@ export function init({
|
||||
dollars += topUpAmount;
|
||||
}
|
||||
|
||||
const close = closes[utils.date.toDateIndex(date)];
|
||||
const close = closes[dateToDateIndex(date)];
|
||||
|
||||
if (!close) return;
|
||||
|
||||
@@ -1071,7 +1079,7 @@ export function init({
|
||||
p1.innerHTML = `After exchanging ${serInvestedAmount} in the span of ${serDaysCount} days, you would have accumulated ${serSats} Satoshis (${serBitcoin} Bitcoin) worth today ${serBitcoinValue} at an average price of ${serAveragePricePaid} per Bitcoin with a return of investment of ${serRoi}, have ${serDollars} left and paid a total of ${serTotalFeesPaid} in fees.`;
|
||||
|
||||
const dayDiff = Math.floor(
|
||||
utils.date.differenceBetween(new Date(), lastInvestDay),
|
||||
differenceBetweenDates(new Date(), lastInvestDay),
|
||||
);
|
||||
const serDailyInvestment = c("emerald", fd(dailyInvestment));
|
||||
const setLastSatsAdded = c("orange", f(lastSatsAdded));
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { createButtonElement, createSelect } from "../core/dom";
|
||||
import { randomFromArray } from "../core/array";
|
||||
import { createButtonElement, createHeader, createSelect } from "../core/dom";
|
||||
import { serdeMetrics, serdeString, serdeUnit } from "../core/serde";
|
||||
import { resetParams } from "../core/url";
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {VecIdToIndexes} args.vecIdToIndexes
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Option} args.option
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Signals} args.signals
|
||||
@@ -10,12 +13,12 @@ import { createButtonElement, createSelect } from "../core/dom";
|
||||
*/
|
||||
function createTable({
|
||||
utils,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
signals,
|
||||
option,
|
||||
vecsResources,
|
||||
}) {
|
||||
const indexToVecIds = createIndexToVecIds(vecIdToIndexes);
|
||||
const indexToMetrics = createIndexToMetrics(metricToIndexes);
|
||||
|
||||
const serializedIndexes = createSerializedIndexes();
|
||||
/** @type {SerializedIndex} */
|
||||
@@ -25,7 +28,7 @@ function createTable({
|
||||
/** @type {SerializedIndex} */ (defaultSerializedIndex),
|
||||
{
|
||||
save: {
|
||||
...utils.serde.string,
|
||||
...serdeString,
|
||||
keyPrefix: "table",
|
||||
key: "index",
|
||||
},
|
||||
@@ -45,20 +48,20 @@ function createTable({
|
||||
|
||||
signals.createEffect(index, (index, prevIndex) => {
|
||||
if (prevIndex !== undefined) {
|
||||
utils.url.resetParams(option);
|
||||
resetParams(option);
|
||||
}
|
||||
|
||||
const possibleVecIds = indexToVecIds[index];
|
||||
const possibleMetrics = indexToMetrics[index];
|
||||
|
||||
const columns = signals.createSignal(/** @type {VecId[]} */ ([]), {
|
||||
const columns = signals.createSignal(/** @type {Metric[]} */ ([]), {
|
||||
equals: false,
|
||||
save: {
|
||||
...utils.serde.vecIds,
|
||||
...serdeMetrics,
|
||||
keyPrefix: `table-${serializedIndex()}`,
|
||||
key: `columns`,
|
||||
},
|
||||
});
|
||||
columns.set((l) => l.filter((id) => possibleVecIds.includes(id)));
|
||||
columns.set((l) => l.filter((id) => possibleMetrics.includes(id)));
|
||||
|
||||
signals.createEffect(columns, (columns) => {
|
||||
console.log(columns);
|
||||
@@ -174,35 +177,35 @@ function createTable({
|
||||
const owner = signals.getOwner();
|
||||
|
||||
/**
|
||||
* @param {VecId} vecId
|
||||
* @param {Metric} metric
|
||||
* @param {number} [_colIndex]
|
||||
*/
|
||||
function addCol(vecId, _colIndex = columns().length) {
|
||||
function addCol(metric, _colIndex = columns().length) {
|
||||
signals.runWithOwner(owner, () => {
|
||||
/** @type {VoidFunction | undefined} */
|
||||
let dispose;
|
||||
signals.createRoot((_dispose) => {
|
||||
dispose = _dispose;
|
||||
|
||||
const vecIdOption = signals.createSignal({
|
||||
name: vecId,
|
||||
value: vecId,
|
||||
const metricOption = signals.createSignal({
|
||||
name: metric,
|
||||
value: metric,
|
||||
});
|
||||
const { select } = createSelect({
|
||||
list: possibleVecIds.map((vecId) => ({
|
||||
name: vecId,
|
||||
value: vecId,
|
||||
list: possibleMetrics.map((metric) => ({
|
||||
name: metric,
|
||||
value: metric,
|
||||
})),
|
||||
signal: vecIdOption,
|
||||
signal: metricOption,
|
||||
});
|
||||
|
||||
signals.createEffect(vecIdOption, (vecIdOption) => {
|
||||
select.style.width = `${21 + 7.25 * vecIdOption.name.length}px`;
|
||||
signals.createEffect(metricOption, (metricOption) => {
|
||||
select.style.width = `${21 + 7.25 * metricOption.name.length}px`;
|
||||
});
|
||||
|
||||
if (_colIndex === columns().length) {
|
||||
columns.set((l) => {
|
||||
l.push(vecId);
|
||||
l.push(metric);
|
||||
return l;
|
||||
});
|
||||
}
|
||||
@@ -252,7 +255,7 @@ function createTable({
|
||||
|
||||
const th = addThCol({
|
||||
select,
|
||||
unit: utils.vecidToUnit(vecId),
|
||||
unit: serdeUnit.deserialize(metric),
|
||||
onLeft: createMoveColumnFunction(false),
|
||||
onRight: createMoveColumnFunction(true),
|
||||
onRemove: () => {
|
||||
@@ -284,23 +287,23 @@ function createTable({
|
||||
}
|
||||
|
||||
signals.createEffect(
|
||||
() => vecIdOption().name,
|
||||
(vecId, prevVecId) => {
|
||||
const unit = utils.vecidToUnit(vecId);
|
||||
() => metricOption().name,
|
||||
(metric, prevMetric) => {
|
||||
const unit = serdeUnit.deserialize(metric);
|
||||
th.setUnit(unit);
|
||||
|
||||
const vec = vecsResources.getOrCreate(index, vecId);
|
||||
const vec = vecsResources.getOrCreate(index, metric);
|
||||
|
||||
vec.fetch({ from, to });
|
||||
|
||||
const fetchedKey = vecsResources.genFetchedKey({ from, to });
|
||||
|
||||
columns.set((l) => {
|
||||
const i = l.indexOf(prevVecId ?? vecId);
|
||||
const i = l.indexOf(prevMetric ?? metric);
|
||||
if (i === -1) {
|
||||
l.push(vecId);
|
||||
l.push(metric);
|
||||
} else {
|
||||
l[i] = vecId;
|
||||
l[i] = metric;
|
||||
}
|
||||
return l;
|
||||
});
|
||||
@@ -325,7 +328,7 @@ function createTable({
|
||||
},
|
||||
);
|
||||
|
||||
return () => vecId;
|
||||
return () => metric;
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -337,10 +340,10 @@ function createTable({
|
||||
});
|
||||
}
|
||||
|
||||
columns().forEach((vecId, colIndex) => addCol(vecId, colIndex));
|
||||
columns().forEach((metric, colIndex) => addCol(metric, colIndex));
|
||||
|
||||
obj.addRandomCol = function () {
|
||||
addCol(utils.array.random(possibleVecIds));
|
||||
addCol(randomFromArray(possibleMetrics));
|
||||
};
|
||||
|
||||
return () => index;
|
||||
@@ -356,7 +359,7 @@ function createTable({
|
||||
* @param {Option} args.option
|
||||
* @param {Elements} args.elements
|
||||
* @param {VecsResources} args.vecsResources
|
||||
* @param {VecIdToIndexes} args.vecIdToIndexes
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
*/
|
||||
export function init({
|
||||
elements,
|
||||
@@ -364,7 +367,7 @@ export function init({
|
||||
option,
|
||||
utils,
|
||||
vecsResources,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
}) {
|
||||
const parent = elements.table;
|
||||
const { headerElement } = createHeader("Table");
|
||||
@@ -376,7 +379,7 @@ export function init({
|
||||
const table = createTable({
|
||||
signals,
|
||||
utils,
|
||||
vecIdToIndexes,
|
||||
metricToIndexes,
|
||||
vecsResources,
|
||||
option,
|
||||
});
|
||||
@@ -397,33 +400,33 @@ export function init({
|
||||
|
||||
function createSerializedIndexes() {
|
||||
return /** @type {const} */ ([
|
||||
/** @satisfies {VecId} */ ("dateindex"),
|
||||
/** @satisfies {VecId} */ ("decadeindex"),
|
||||
/** @satisfies {VecId} */ ("difficultyepoch"),
|
||||
/** @satisfies {VecId} */ ("emptyoutputindex"),
|
||||
/** @satisfies {VecId} */ ("halvingepoch"),
|
||||
/** @satisfies {VecId} */ ("height"),
|
||||
/** @satisfies {VecId} */ ("inputindex"),
|
||||
/** @satisfies {VecId} */ ("monthindex"),
|
||||
/** @satisfies {VecId} */ ("opreturnindex"),
|
||||
/** @satisfies {VecId} */ ("semesterindex"),
|
||||
/** @satisfies {VecId} */ ("outputindex"),
|
||||
/** @satisfies {VecId} */ ("p2aaddressindex"),
|
||||
/** @satisfies {VecId} */ ("p2msoutputindex"),
|
||||
/** @satisfies {VecId} */ ("p2pk33addressindex"),
|
||||
/** @satisfies {VecId} */ ("p2pk65addressindex"),
|
||||
/** @satisfies {VecId} */ ("p2pkhaddressindex"),
|
||||
/** @satisfies {VecId} */ ("p2shaddressindex"),
|
||||
/** @satisfies {VecId} */ ("p2traddressindex"),
|
||||
/** @satisfies {VecId} */ ("p2wpkhaddressindex"),
|
||||
/** @satisfies {VecId} */ ("p2wshaddressindex"),
|
||||
/** @satisfies {VecId} */ ("quarterindex"),
|
||||
/** @satisfies {VecId} */ ("txindex"),
|
||||
/** @satisfies {VecId} */ ("unknownoutputindex"),
|
||||
/** @satisfies {VecId} */ ("weekindex"),
|
||||
/** @satisfies {VecId} */ ("yearindex"),
|
||||
/** @satisfies {VecId} */ ("loadedaddressindex"),
|
||||
/** @satisfies {VecId} */ ("emptyaddressindex"),
|
||||
/** @satisfies {Metric} */ ("dateindex"),
|
||||
/** @satisfies {Metric} */ ("decadeindex"),
|
||||
/** @satisfies {Metric} */ ("difficultyepoch"),
|
||||
/** @satisfies {Metric} */ ("emptyoutputindex"),
|
||||
/** @satisfies {Metric} */ ("halvingepoch"),
|
||||
/** @satisfies {Metric} */ ("height"),
|
||||
/** @satisfies {Metric} */ ("inputindex"),
|
||||
/** @satisfies {Metric} */ ("monthindex"),
|
||||
/** @satisfies {Metric} */ ("opreturnindex"),
|
||||
/** @satisfies {Metric} */ ("semesterindex"),
|
||||
/** @satisfies {Metric} */ ("outputindex"),
|
||||
/** @satisfies {Metric} */ ("p2aaddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2msoutputindex"),
|
||||
/** @satisfies {Metric} */ ("p2pk33addressindex"),
|
||||
/** @satisfies {Metric} */ ("p2pk65addressindex"),
|
||||
/** @satisfies {Metric} */ ("p2pkhaddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2shaddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2traddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2wpkhaddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2wshaddressindex"),
|
||||
/** @satisfies {Metric} */ ("quarterindex"),
|
||||
/** @satisfies {Metric} */ ("txindex"),
|
||||
/** @satisfies {Metric} */ ("unknownoutputindex"),
|
||||
/** @satisfies {Metric} */ ("weekindex"),
|
||||
/** @satisfies {Metric} */ ("yearindex"),
|
||||
/** @satisfies {Metric} */ ("loadedaddressindex"),
|
||||
/** @satisfies {Metric} */ ("emptyaddressindex"),
|
||||
]);
|
||||
}
|
||||
/** @typedef {ReturnType<typeof createSerializedIndexes>} SerializedIndexes */
|
||||
@@ -493,24 +496,24 @@ function serializedIndexToIndex(serializedIndex) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {VecIdToIndexes} vecIdToIndexes
|
||||
* @param {MetricToIndexes} metricToIndexes
|
||||
*/
|
||||
function createIndexToVecIds(vecIdToIndexes) {
|
||||
const indexToVecIds = Object.entries(vecIdToIndexes).reduce(
|
||||
function createIndexToMetrics(metricToIndexes) {
|
||||
const indexToMetrics = Object.entries(metricToIndexes).reduce(
|
||||
(arr, [_id, indexes]) => {
|
||||
const id = /** @type {VecId} */ (_id);
|
||||
const id = /** @type {Metric} */ (_id);
|
||||
indexes.forEach((i) => {
|
||||
arr[i] ??= [];
|
||||
arr[i].push(id);
|
||||
});
|
||||
return arr;
|
||||
},
|
||||
/** @type {VecId[][]} */ (Array.from({ length: 24 })),
|
||||
/** @type {Metric[][]} */ (Array.from({ length: 24 })),
|
||||
);
|
||||
indexToVecIds.forEach((arr) => {
|
||||
indexToMetrics.forEach((arr) => {
|
||||
arr.sort();
|
||||
});
|
||||
return indexToVecIds;
|
||||
return indexToMetrics;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user