diff --git a/crates/brk_fetcher/examples/main.rs b/crates/brk_fetcher/examples/main.rs index fc9bb620d..9a79801e7 100644 --- a/crates/brk_fetcher/examples/main.rs +++ b/crates/brk_fetcher/examples/main.rs @@ -1,5 +1,5 @@ use brk_core::{Date, Height}; -use brk_fetcher::{BRK, Fetcher}; +use brk_fetcher::{BRK, Binance, Fetcher, Kraken}; fn main() -> color_eyre::Result<()> { color_eyre::install()?; @@ -12,6 +12,19 @@ fn main() -> color_eyre::Result<()> { let mut fetcher = Fetcher::import(None)?; + Binance::fetch_1d().map(|b| { + dbg!(b.last_key_value()); + }); + Kraken::fetch_1d().map(|b| { + dbg!(b.last_key_value()); + }); + Binance::fetch_1mn().map(|b| { + dbg!(b.last_key_value()); + }); + Kraken::fetch_1mn().map(|b| { + dbg!(b.last_key_value()); + }); + dbg!(fetcher.get_date(Date::new(2025, 6, 5))?); dbg!(fetcher.get_height( 899911_u32.into(), diff --git a/crates/brk_fetcher/src/fetchers/kraken.rs b/crates/brk_fetcher/src/fetchers/kraken.rs index 8cd5c4960..ff3d4416a 100644 --- a/crates/brk_fetcher/src/fetchers/kraken.rs +++ b/crates/brk_fetcher/src/fetchers/kraken.rs @@ -32,7 +32,7 @@ impl Kraken { ) } - fn fetch_1mn() -> color_eyre::Result> { + pub fn fetch_1mn() -> color_eyre::Result> { info!("Fetching 1mn prices from Kraken..."); retry( @@ -54,7 +54,7 @@ impl Kraken { .ok_or(color_eyre::eyre::Error::msg("Couldn't find date")) } - fn fetch_1d() -> color_eyre::Result> { + pub fn fetch_1d() -> color_eyre::Result> { info!("Fetching daily prices from Kraken..."); retry( diff --git a/crates/brk_fetcher/src/lib.rs b/crates/brk_fetcher/src/lib.rs index 88a284ed5..9d7f10a57 100644 --- a/crates/brk_fetcher/src/lib.rs +++ b/crates/brk_fetcher/src/lib.rs @@ -40,11 +40,11 @@ impl Fetcher { } fn get_date_(&mut self, date: Date, tries: usize) -> color_eyre::Result { - self.binance + self.kraken .get_from_1d(&date) .or_else(|_| { // eprintln!("{e}"); - self.kraken.get_from_1d(&date) + self.binance.get_from_1d(&date) }) .or_else(|_| { // eprintln!("{e}"); @@ -90,11 +90,11 @@ impl Fetcher { let previous_timestamp = previous_timestamp.map(|t| t.floor_seconds()); let ohlc = self - .binance + .kraken .get_from_1mn(timestamp, previous_timestamp) .unwrap_or_else(|_report| { // eprintln!("{_report}"); - self.kraken + self.binance .get_from_1mn(timestamp, previous_timestamp) .unwrap_or_else(|_report| { // // eprintln!("{_report}"); @@ -185,8 +185,8 @@ How to fix this: } pub fn clear(&mut self) { + self.kraken.clear(); self.binance.clear(); self.brk.clear(); - self.kraken.clear(); } } diff --git a/websites/default/packages/lightweight-charts/wrapper.js b/websites/default/packages/lightweight-charts/wrapper.js index 98f0564b2..4d743f277 100644 --- a/websites/default/packages/lightweight-charts/wrapper.js +++ b/websites/default/packages/lightweight-charts/wrapper.js @@ -18,6 +18,7 @@ * @property {string} id * @property {Signal} active * @property {Signal} hasData + * @property {Signal} url * @property {VoidFunction} remove */ @@ -128,7 +129,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { } : {}), // ..._options, - }) + }), ); ichart.priceScale("right").applyOptions({ @@ -160,7 +161,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { }, }, }); - } + }, ); let timeScaleSet = false; @@ -172,12 +173,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { index === /** @satisfies {MonthIndex} */ (7) ? 1 : index === /** @satisfies {QuarterIndex} */ (19) - ? 3 - : index === /** @satisfies {YearIndex} */ (23) - ? 12 - : index === /** @satisfies {DecadeIndex} */ (1) - ? 120 - : 0.5; + ? 3 + : index === /** @satisfies {YearIndex} */ (23) + ? 12 + : index === /** @satisfies {DecadeIndex} */ (1) + ? 120 + : 0.5; ichart.applyOptions({ timeScale: { @@ -200,12 +201,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { activeResources.forEach((v) => { v.fetch(); }); - }) + }), ); if (fitContent) { new ResizeObserver(() => ichart.timeScale().fitContent()).observe( - chartDiv + chartDiv, ); } @@ -234,7 +235,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { const children = Array.from(parent.childNodes).filter( (element) => /** @type {HTMLElement} */ (element).dataset.position === - position + position, ); if (children.length === 1) { @@ -256,7 +257,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { fieldset.append(createChild(pane)); }), - paneIndex ? 50 : 0 + paneIndex ? 50 : 0, ); } @@ -336,13 +337,11 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { const hasData = signals.createSignal(false); - let url = /** @type {string | undefined} */ (undefined); - signals.createEffect(active, (active) => // Or remove ? iseries.applyOptions({ visible: active, - }) + }), ); iseries.setSeriesOrder(order); @@ -356,6 +355,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { active, hasData, id, + url: signals.createSignal(/** @type {string | null} */ (null)), remove() { dispose(); // @ts-ignore @@ -372,14 +372,15 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { index, index === /** @satisfies {Height} */ (5) ? "timestamp-fixed" - : "timestamp" + : "timestamp", ); timeResource.fetch(); const valuesResource = vecsResources.getOrCreate(index, vecId); _valuesResource = valuesResource; - url = valuesResource.url; + series.url.set(() => valuesResource.url); + signals.createEffect(active, (active) => { if (active) { valuesResource.fetch(); @@ -478,7 +479,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { } }); timeScaleSet = true; - } + }, ); } else { activeResources.delete(valuesResource); @@ -505,7 +506,6 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { series, name, colors, - url, order, }); @@ -574,7 +574,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { borderVisible: false, visible: defaultActive !== false, }, - paneIndex + paneIndex, ) ); @@ -630,7 +630,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { color: color(), ...options, }, - paneIndex + paneIndex, ) ); @@ -698,7 +698,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { topFillColor2: "transparent", lineVisible: true, }, - paneIndex + paneIndex, ) ); @@ -790,9 +790,8 @@ function createLegend({ signals, utils }) { * @param {string} args.name * @param {number} args.order * @param {Color[]} args.colors - * @param {string} [args.url] */ - addOrReplace({ series, name, colors, url, order }) { + addOrReplace({ series, name, colors, order }) { const div = window.document.createElement("div"); const prev = legends[order]; @@ -868,7 +867,7 @@ function createLegend({ signals, utils }) { } else { spanColor.style.backgroundColor = tameColor(color); } - } + }, ); }); @@ -897,14 +896,17 @@ function createLegend({ signals, utils }) { } }); - if (url) { - const anchor = window.document.createElement("a"); - anchor.href = url; - anchor.target = "_blank"; - anchor.rel = "noopener noreferrer"; - anchor.title = "Click to view data"; - div.append(anchor); - } + const anchor = window.document.createElement("a"); + + signals.createEffect(series.url, (url) => { + if (url) { + anchor.href = url; + anchor.target = "_blank"; + anchor.rel = "noopener noreferrer"; + anchor.title = "Click to view data"; + div.append(anchor); + } + }); }, /** * @param {number} start @@ -1025,17 +1027,17 @@ function numberToShortUSFormat(value, digits) { if (modulused === 0) { return `${numberToUSFormat( value / (1_000_000 * 1_000 ** letterIndex), - 3 + 3, )}${letter}`; } else if (modulused === 1) { return `${numberToUSFormat( value / (1_000_000 * 1_000 ** letterIndex), - 2 + 2, )}${letter}`; } else { return `${numberToUSFormat( value / (1_000_000 * 1_000 ** letterIndex), - 1 + 1, )}${letter}`; } } @@ -1085,7 +1087,7 @@ function createOklchToRGBA() { return rgb.map((c) => Math.abs(c) > 0.0031308 ? (c < 0 ? -1 : 1) * (1.055 * Math.abs(c) ** (1 / 2.4) - 0.055) - : 12.92 * c + : 12.92 * c, ); } /** @@ -1097,7 +1099,7 @@ function createOklchToRGBA() { 1, 0.3963377773761749, 0.2158037573099136, 1, -0.1055613458156586, -0.0638541728258133, 1, -0.0894841775298119, -1.2914855480194092, ]), - lab + lab, ); const LMS = /** @type {[number, number, number]} */ ( LMSg.map((val) => val ** 3) @@ -1108,7 +1110,7 @@ function createOklchToRGBA() { -0.0405757452148008, 1.112286803280317, -0.0717110580655164, -0.0763729366746601, -0.4214933324022432, 1.5869240198367816, ]), - LMS + LMS, ); } /** @@ -1121,7 +1123,7 @@ function createOklchToRGBA() { -0.9692436362808796, 1.8759675015077202, 0.04155505740717559, 0.05563007969699366, -0.20397695888897652, 1.0569715142428786, ], - xyz + xyz, ); } @@ -1144,8 +1146,8 @@ function createOklchToRGBA() { }); const rgb = srgbLinear2rgb( xyz2rgbLinear( - oklab2xyz(oklch2oklab(/** @type {[number, number, number]} */ (lch))) - ) + oklab2xyz(oklch2oklab(/** @type {[number, number, number]} */ (lch))), + ), ).map((v) => { return Math.max(Math.min(Math.round(v * 255), 255), 0); }); diff --git a/websites/default/scripts/main.js b/websites/default/scripts/main.js index 9a8dd2a6c..8aa8bac78 100644 --- a/websites/default/scripts/main.js +++ b/websites/default/scripts/main.js @@ -67,14 +67,14 @@ function initPackages() { const imports = { async signals() { return import("../packages/solid-signals/wrapper.js").then((d) => - d.default.then((d) => d) + d.default.then((d) => d), ); }, async lightweightCharts() { return window.document.fonts.ready.then(() => import("../packages/lightweight-charts/wrapper.js").then((d) => - d.default.then((d) => d) - ) + d.default.then((d) => d), + ), ); }, async leanQr() { @@ -82,7 +82,7 @@ function initPackages() { }, async ufuzzy() { return import("../packages/ufuzzy/v1.0.18/script.js").then( - ({ default: d }) => d + ({ default: d }) => d, ); }, }; @@ -596,7 +596,7 @@ function createUtils() { window.history.pushState( null, "", - `${pathname}?${urlParams.toString()}` + `${pathname}?${urlParams.toString()}`, ); } catch (_) {} }, @@ -613,7 +613,7 @@ function createUtils() { window.history.replaceState( null, "", - `${pathname}?${urlParams.toString()}` + `${pathname}?${urlParams.toString()}`, ); } catch (_) {} }, @@ -1246,8 +1246,8 @@ function createUtils() { today.getUTCDate(), 0, 0, - 0 - ) + 0, + ), ); }, /** @@ -1349,7 +1349,7 @@ function createUtils() { */ function getNumberOfDaysBetweenTwoDates(oldest, youngest) { return Math.round( - Math.abs((youngest.getTime() - oldest.getTime()) / date.ONE_DAY_IN_MS) + Math.abs((youngest.getTime() - oldest.getTime()) / date.ONE_DAY_IN_MS), ); } @@ -1567,7 +1567,7 @@ function createVecsResources(signals, utils) { const fetchedRecord = signals.createSignal( /** @type {Map}>} */ ( new Map() - ) + ), ); return { @@ -1615,7 +1615,7 @@ function createVecsResources(signals, utils) { index, id, from, - to + to, ) ); fetched.at = new Date(); @@ -1876,7 +1876,7 @@ function initWebSockets(signals, utils) { window.document.addEventListener( "visibilitychange", - reinitWebSocketIfDocumentNotHidden + reinitWebSocketIfDocumentNotHidden, ); window.document.addEventListener("online", reinitWebSocket); @@ -1885,7 +1885,7 @@ function initWebSockets(signals, utils) { ws?.close(); window.document.removeEventListener( "visibilitychange", - reinitWebSocketIfDocumentNotHidden + reinitWebSocketIfDocumentNotHidden, ); window.document.removeEventListener("online", reinitWebSocket); live.set(false); @@ -1911,7 +1911,7 @@ function initWebSockets(signals, utils) { symbol: ["BTC/USD"], interval: 1440, }, - }) + }), ); }); @@ -1941,7 +1941,7 @@ function initWebSockets(signals, utils) { /** @type {ReturnType>} */ const kraken1dCandle = createWebsocket((callback) => - krakenCandleWebSocketCreator(callback) + krakenCandleWebSocketCreator(callback), ); kraken1dCandle.open(); @@ -2000,7 +2000,7 @@ function main() { } const frame = window.document.getElementById( - /** @type {string} */ (input.value) + /** @type {string} */ (input.value), ); if (!frame) { @@ -2098,23 +2098,23 @@ function main() { function initDark() { const preferredColorSchemeMatchMedia = window.matchMedia( - "(prefers-color-scheme: dark)" + "(prefers-color-scheme: dark)", ); const dark = signals.createSignal( - preferredColorSchemeMatchMedia.matches + preferredColorSchemeMatchMedia.matches, ); preferredColorSchemeMatchMedia.addEventListener( "change", ({ matches }) => { dark.set(matches); - } + }, ); return dark; } const dark = initDark(); const qrcode = signals.createSignal( - /** @type {string | null} */ (null) + /** @type {string | null} */ (null), ); function createLastHeightResource() { @@ -2125,7 +2125,7 @@ function main() { lastHeight.set(h); }, /** @satisfies {Height} */ (5), - "height" + "height", ); } fetchLastHeight(); @@ -2145,21 +2145,18 @@ function main() { env, signals, utils, - webSockets, qrcode, }); - // const urlSelected = utils.url.pathnameToSelectedId(); - // function createWindowPopStateEvent() { - // window.addEventListener("popstate", (event) => { - // const urlSelected = utils.url.pathnameToSelectedId(); - // const option = options.list.find((option) => urlSelected === option.id); - // if (option) { - // options.selected.set(option); - // } - // }); - // } - // createWindowPopStateEvent(); + // window.addEventListener("popstate", (_) => { + // const urlSelected = utils.url.pathnameToSelectedId(); + // const option = options.list.find( + // (option) => urlSelected === option.id, + // ); + // if (option) { + // options.selected.set(option); + // } + // }); function initSelected() { let firstRun = true; @@ -2172,10 +2169,10 @@ function main() { const owner = signals.getOwner(); const chartOption = signals.createSignal( - /** @type {ChartOption | null} */ (null) + /** @type {ChartOption | null} */ (null), ); const simOption = signals.createSignal( - /** @type {SimulationOption | null} */ (null) + /** @type {SimulationOption | null} */ (null), ); let previousElement = /** @type {HTMLElement | undefined} */ ( @@ -2186,6 +2183,7 @@ function main() { let firstTimeLoadingSimulation = true; signals.createEffect(options.selected, (option) => { + console.log(utils.url.pathnameToSelectedId(), option.id); if (previousElement) { previousElement.hidden = true; utils.url.resetParams(option); @@ -2222,10 +2220,10 @@ function main() { webSockets, vecsResources, vecIdToIndexes, - }) - ) - ) - ) + }), + ), + ), + ), ); } firstTimeLoadingChart = false; @@ -2248,9 +2246,9 @@ function main() { vecsResources, option, vecIdToIndexes, - }) - ) - ) + }), + ), + ), ); } firstTimeLoadingTable = false; @@ -2278,10 +2276,10 @@ function main() { signals, utils, vecsResources, - }) - ) - ) - ) + }), + ), + ), + ), ); } firstTimeLoadingSimulation = false; @@ -2310,7 +2308,7 @@ function main() { createMobileSwitchEffect(); utils.dom.onFirstIntersection(elements.aside, () => - signals.runWithOwner(owner, initSelectedFrame) + signals.runWithOwner(owner, initSelectedFrame), ); } initSelected(); @@ -2388,7 +2386,7 @@ function main() { if (indexes?.length) { const maxIndex = Math.min( (order || indexes).length - 1, - minIndex + RESULTS_PER_PAGE - 1 + minIndex + RESULTS_PER_PAGE - 1, ); list = Array(maxIndex - minIndex + 1); @@ -2464,7 +2462,7 @@ function main() { haystack, needle, undefined, - infoThresh + infoThresh, ); if (!result?.[0]?.length || !result?.[1]) { @@ -2472,7 +2470,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh + infoThresh, ); } @@ -2481,7 +2479,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh + infoThresh, ); } @@ -2490,7 +2488,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh + infoThresh, ); } @@ -2499,7 +2497,7 @@ function main() { haystack, needle, undefined, - infoThresh + infoThresh, ); } @@ -2508,7 +2506,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh + infoThresh, ); } @@ -2591,7 +2589,7 @@ function main() { shareDiv.hidden = false; }); - }) + }), ); } initShare(); @@ -2613,7 +2611,7 @@ function main() { localStorage.setItem(barWidthLocalStorageKey, String(width)); } else { elements.main.style.width = elements.style.getPropertyValue( - "--default-main-width" + "--default-main-width", ); localStorage.removeItem(barWidthLocalStorageKey); } @@ -2649,9 +2647,9 @@ function main() { window.addEventListener("mouseleave", setResizeFalse); } initDesktopResizeBar(); - }) - ) - ) + }), + ), + ), ); } main(); diff --git a/websites/default/scripts/options.js b/websites/default/scripts/options.js index b7962c1d5..f5aabb72b 100644 --- a/websites/default/scripts/options.js +++ b/websites/default/scripts/options.js @@ -1430,7 +1430,7 @@ function createPartialOptions(colors) { key: `${fixKey(key)}realized-price`, name, color, - }) + }), ), } : createPriceWithRatio({ @@ -1513,7 +1513,7 @@ function createPartialOptions(colors) { /** @satisfies {FetchedBaselineSeriesBlueprint} */ ({ type: "Baseline", key: `${fixKey( - key + key, )}net-realized-profit-and-loss-relative-to-realized-cap`, title: useGroupName ? name : "Net", color: useGroupName ? color : undefined, @@ -1584,7 +1584,7 @@ function createPartialOptions(colors) { /** @satisfies {FetchedBaselineSeriesBlueprint} */ ({ type: "Baseline", key: `${fixKey( - key + key, )}adjusted-spent-output-profit-ratio`, title: useGroupName ? name : "asopr", color: useGroupName ? color : undefined, @@ -1607,7 +1607,7 @@ function createPartialOptions(colors) { key: `${fixKey(key)}sell-side-risk-ratio`, name: useGroupName ? name : "Risk", color: color, - }) + }), ), }, ], @@ -1696,7 +1696,7 @@ function createPartialOptions(colors) { /** @satisfies {FetchedBaselineSeriesBlueprint} */ ({ type: "Baseline", key: `${fixKey( - key + key, )}net-unrealized-profit-and-loss-relative-to-market-cap`, title: useGroupName ? name : "Net", color: useGroupName ? color : undefined, @@ -1874,7 +1874,7 @@ function createPartialOptions(colors) { key: `${key}-sma`, name: key, color, - }) + }), ), }, ...averages.map(({ key, name, color }) => @@ -1884,7 +1884,7 @@ function createPartialOptions(colors) { title: `${name} Market Price Moving Average`, legend: "average", color, - }) + }), ), ], }, @@ -1975,7 +1975,7 @@ function createPartialOptions(colors) { }, }), ], - }) + }), ), .../** @type {const} */ ([ { name: "2 Year", key: "2y" }, @@ -1993,7 +1993,7 @@ function createPartialOptions(colors) { top: [ createBaseSeries({ key: `${key}-dca-avg-price`, - name: `dca avg. price`, + name: `dca`, color: colors.orange, }), createBaseSeries({ @@ -2046,7 +2046,7 @@ function createPartialOptions(colors) { }, }), ], - }) + }), ), ], }, @@ -2062,7 +2062,7 @@ function createPartialOptions(colors) { name: `${year}`, color, defaultActive, - }) + }), ), }, ...dcaClasses.map( @@ -2089,7 +2089,7 @@ function createPartialOptions(colors) { }, }), ], - }) + }), ), ], }, @@ -2150,10 +2150,10 @@ function createPartialOptions(colors) { bottom: [ ...createAverageSumCumulativeMinMaxPercentilesSeries("fee"), ...createAverageSumCumulativeMinMaxPercentilesSeries( - "fee-in-btc" + "fee-in-btc", ), ...createAverageSumCumulativeMinMaxPercentilesSeries( - "fee-in-usd" + "fee-in-usd", ), ], }, @@ -2889,17 +2889,9 @@ function createPartialOptions(colors) { * @param {Signals} args.signals * @param {Env} args.env * @param {Utilities} args.utils - * @param {WebSockets} args.webSockets * @param {Signal} args.qrcode */ -export function initOptions({ - colors, - signals, - env, - utils, - webSockets, - qrcode, -}) { +export function initOptions({ colors, signals, env, utils, qrcode }) { const LS_SELECTED_KEY = `selected-id`; const urlSelected = utils.url.pathnameToSelectedId(); @@ -2917,7 +2909,7 @@ export function initOptions({ const detailsList = []; const treeElement = signals.createSignal( - /** @type {HTMLDivElement | null} */ (null) + /** @type {HTMLDivElement | null} */ (null), ); /** @type {string[] | undefined} */ @@ -3036,7 +3028,7 @@ export function initOptions({ return null; } }, - null + null, ); partialTree.forEach((anyPartial, partialIndex) => { @@ -3059,7 +3051,7 @@ export function initOptions({ if ("tree" in anyPartial) { const folderId = utils.stringToId( - `${(path || []).join(" ")} ${anyPartial.name} folder` + `${(path || []).join(" ")} ${anyPartial.name} folder`, ); /** @type {Omit} */ @@ -3074,13 +3066,13 @@ export function initOptions({ const thisPath = groupAddons.id; const passedDetails = signals.createSignal( - /** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null) + /** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null), ); const childOptionsCount = recursiveProcessPartialTree( anyPartial.tree, passedDetails, - [...(path || []), thisPath] + [...(path || []), thisPath], ); listForSum.push(childOptionsCount); @@ -3212,7 +3204,7 @@ export function initOptions({ }); return signals.createMemo(() => - listForSum.reduce((acc, s) => acc + s(), 0) + listForSum.reduce((acc, s) => acc + s(), 0), ); } recursiveProcessPartialTree(partialOptions, treeElement); @@ -3239,7 +3231,7 @@ export function initOptions({ console.log( [...m.entries()] .filter(([_, value]) => value > 1) - .map(([key, _]) => key) + .map(([key, _]) => key), ); throw Error("ID duplicate"); diff --git a/websites/default/scripts/vecid-to-indexes.js b/websites/default/scripts/vecid-to-indexes.js index 95886652d..9dfb8c649 100644 --- a/websites/default/scripts/vecid-to-indexes.js +++ b/websites/default/scripts/vecid-to-indexes.js @@ -2,7 +2,7 @@ // File auto-generated, any modifications will be overwritten // -export const VERSION = "v0.0.52"; +export const VERSION = "v0.0.54"; /** @typedef {0} DateIndex */ /** @typedef {1} DecadeIndex */