diff --git a/crates/brk_cli/src/bridge.rs b/crates/brk_cli/src/bridge.rs index 40921a921..4b407dc4b 100644 --- a/crates/brk_cli/src/bridge.rs +++ b/crates/brk_cli/src/bridge.rs @@ -124,13 +124,13 @@ return { contents += " };\n}\n"; contents += " -/** @typedef {ReturnType} VecIdToIndexes -/** @typedef {keyof VecIdToIndexes} VecId */ +/** @typedef {ReturnType} MetricToIndexes +/** @typedef {keyof MetricToIndexes} Metric */ /** * @returns {Record} */ -export function createVecIdToIndexes() { +export function createMetricToIndexes() { return { "; diff --git a/websites/bitview/scripts/core/api.js b/websites/bitview/scripts/core/api.js index 034b4bbbe..44b4a57fb 100644 --- a/websites/bitview/scripts/core/api.js +++ b/websites/bitview/scripts/core/api.js @@ -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)); }, }; diff --git a/websites/bitview/scripts/core/array.js b/websites/bitview/scripts/core/array.js index 8e11235cf..e867443a1 100644 --- a/websites/bitview/scripts/core/array.js +++ b/websites/bitview/scripts/core/array.js @@ -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)]; } diff --git a/websites/bitview/scripts/core/chart.js b/websites/bitview/scripts/core/chart.js index 06b13ffd6..5f97178c5 100644 --- a/websites/bitview/scripts/core/chart.js +++ b/websites/bitview/scripts/core/chart.js @@ -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[]>} [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} [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} [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} [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} [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, }); }, }; diff --git a/websites/bitview/scripts/core/date.js b/websites/bitview/scripts/core/date.js index a9d010184..ca3de4588 100644 --- a/websites/bitview/scripts/core/date.js +++ b/websites/bitview/scripts/core/date.js @@ -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)); } diff --git a/websites/bitview/scripts/core/dom.js b/websites/bitview/scripts/core/dom.js index eb20f97fc..2c49ab4cd 100644 --- a/websites/bitview/scripts/core/dom.js +++ b/websites/bitview/scripts/core/dom.js @@ -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} */ const selected = signals.createSignal(defaultValue, { save: { - ...stringSerde, + ...serdeString, keyPrefix: keyPrefix ?? "", key, saveDefaultValue: true, diff --git a/websites/bitview/scripts/core/options/full.js b/websites/bitview/scripts/core/options/full.js index 78129ca16..40994189f 100644 --- a/websites/bitview/scripts/core/options/full.js +++ b/websites/bitview/scripts/core/options/full.js @@ -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} 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); } diff --git a/websites/bitview/scripts/core/options/partial.js b/websites/bitview/scripts/core/options/partial.js index bb0203be9..8453b697c 100644 --- a/websites/bitview/scripts/core/options/partial.js +++ b/websites/bitview/scripts/core/options/partial.js @@ -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} StartsWith + * @typedef {Extract} StartsWith */ /** * @template {string} S - * @typedef {Extract} EndsWith + * @typedef {Extract} EndsWith */ /** * @template {string} K @@ -155,32 +155,32 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) { /** * @typedef {"cumulative_"} CumulativePrefix * @typedef {"_30d_delta"} _30DChageSubString - * @typedef {StartsWith} CumulativeVecId - * @typedef {ExcludeSubstring, _30DChageSubString>} CumulativeVecIdBase + * @typedef {StartsWith} CumulativeMetric + * @typedef {ExcludeSubstring, _30DChageSubString>} CumulativeMetricBase * @typedef {"_avg"} AverageSuffix - * @typedef {EndsWith} VecIdAverage - * @typedef {WithoutSuffix} VecIdAverageBase + * @typedef {EndsWith} MetricAverage + * @typedef {WithoutSuffix} MetricAverageBase * @typedef {"_median"} MedianSuffix - * @typedef {EndsWith} VecIdMedian - * @typedef {WithoutSuffix} VecIdMedianBase + * @typedef {EndsWith} MetricMedian + * @typedef {WithoutSuffix} MetricMedianBase * @typedef {"_pct90"} _pct90Suffix - * @typedef {EndsWith<_pct90Suffix>} VecIdpct90 - * @typedef {WithoutSuffix} VecIdpct90Base + * @typedef {EndsWith<_pct90Suffix>} MetricPct90 + * @typedef {WithoutSuffix} MetricPct90Base * @typedef {"_pct75"} _pct75Suffix - * @typedef {EndsWith<_pct75Suffix>} VecIdpct75 - * @typedef {WithoutSuffix} VecIdpct75Base + * @typedef {EndsWith<_pct75Suffix>} MetricPct75 + * @typedef {WithoutSuffix} MetricPct75Base * @typedef {"_pct25"} _pct25Suffix - * @typedef {EndsWith<_pct25Suffix>} VecIdpct25 - * @typedef {WithoutSuffix} VecIdpct25Base + * @typedef {EndsWith<_pct25Suffix>} MetricPct25 + * @typedef {WithoutSuffix} MetricPct25Base * @typedef {"_pct10"} _pct10Suffix - * @typedef {EndsWith<_pct10Suffix>} VecIdpct10 - * @typedef {WithoutSuffix} VecIdpct10Base + * @typedef {EndsWith<_pct10Suffix>} MetricPct10 + * @typedef {WithoutSuffix} MetricPct10Base * @typedef {"_max"} MaxSuffix - * @typedef {EndsWith} VecIdMax - * @typedef {WithoutSuffix} VecIdMaxBase + * @typedef {EndsWith} MetricMax + * @typedef {WithoutSuffix} MetricMaxBase * @typedef {"_min"} MinSuffix - * @typedef {EndsWith} VecIdMin - * @typedef {WithoutSuffix} VecIdMinBase + * @typedef {EndsWith} MetricMin + * @typedef {WithoutSuffix} 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} VecIdRatioZScoreCap - * @typedef {WithoutSuffix} VecIdRatioZScoreCapBase + * @typedef {EndsWith} MetricRatioZScoreCap + * @typedef {WithoutSuffix} 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} VecIdSupplyInProfit - * @typedef {WithoutSuffix} CohortId + * @typedef {EndsWith} MetricSupplyInProfit + * @typedef {WithoutSuffix} 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`, diff --git a/websites/bitview/scripts/core/serde.js b/websites/bitview/scripts/core/serde.js index 6a945f166..aa8e63298 100644 --- a/websites/bitview/scripts/core/serde.js +++ b/websites/bitview/scripts/core/serde.js @@ -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} */ diff --git a/websites/bitview/scripts/core/storage.js b/websites/bitview/scripts/core/storage.js index bb74dcd72..cecebbcad 100644 --- a/websites/bitview/scripts/core/storage.js +++ b/websites/bitview/scripts/core/storage.js @@ -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); +} diff --git a/websites/bitview/scripts/core/scheduling.js b/websites/bitview/scripts/core/timing.js similarity index 100% rename from websites/bitview/scripts/core/scheduling.js rename to websites/bitview/scripts/core/timing.js diff --git a/websites/bitview/scripts/core/url.js b/websites/bitview/scripts/core/url.js index f5c7c1c03..d77ad8673 100644 --- a/websites/bitview/scripts/core/url.js +++ b/websites/bitview/scripts/core/url.js @@ -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); +} diff --git a/websites/bitview/scripts/entry.js b/websites/bitview/scripts/entry.js index 291932066..46a42d366 100644 --- a/websites/bitview/scripts/entry.js +++ b/websites/bitview/scripts/entry.js @@ -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` diff --git a/websites/bitview/scripts/main.js b/websites/bitview/scripts/main.js index efe69c32b..8ee65c8a4 100644 --- a/websites/bitview/scripts/main.js +++ b/websites/bitview/scripts/main.js @@ -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 (_) {} } diff --git a/websites/bitview/scripts/panes/chart.js b/websites/bitview/scripts/panes/chart.js index b53aafe91..8bc3916e6 100644 --- a/websites/bitview/scripts/panes/chart.js +++ b/websites/bitview/scripts/panes/chart.js @@ -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} 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 }; diff --git a/websites/bitview/scripts/panes/explorer.js b/websites/bitview/scripts/panes/explorer.js index 395c9ff6c..f07c3e66b 100644 --- a/websites/bitview/scripts/panes/explorer.js +++ b/websites/bitview/scripts/panes/explorer.js @@ -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(); diff --git a/websites/bitview/scripts/panes/simulation.js b/websites/bitview/scripts/panes/simulation.js index f1936264f..ed6cd7946 100644 --- a/websites/bitview/scripts/panes/simulation.js +++ b/websites/bitview/scripts/panes/simulation.js @@ -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)); diff --git a/websites/bitview/scripts/panes/table.js b/websites/bitview/scripts/panes/table.js index 1a1d749ff..274cdc503 100644 --- a/websites/bitview/scripts/panes/table.js +++ b/websites/bitview/scripts/panes/table.js @@ -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} 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; } /**