// @ts-check /** * @typedef {Height | Dateindex | Weekindex | Difficultyepoch | Monthindex | Quarterindex | Yearindex | Decadeindex | Halvingepoch} ChartableIndex */ /** * @template {readonly unknown[]} T * @typedef {Extract extends never ? false : true} IncludesChartableIndex */ /** * @typedef {{[K in VecId]: IncludesChartableIndex extends true ? K : never}[VecId]} ChartableVecId */ /** * @typedef {Object} BaseSeriesBlueprint * @property {string} title * @property {boolean} [defaultActive] * * @typedef {Object} BaselineSeriesBlueprintSpecific * @property {"Baseline"} type * @property {Color} [color] * @property {DeepPartial} [options] * @property {Accessor} [data] * @typedef {BaseSeriesBlueprint & BaselineSeriesBlueprintSpecific} BaselineSeriesBlueprint * * @typedef {Object} CandlestickSeriesBlueprintSpecific * @property {"Candlestick"} type * @property {Color} [color] * @property {DeepPartial} [options] * @property {Accessor} [data] * @typedef {BaseSeriesBlueprint & CandlestickSeriesBlueprintSpecific} CandlestickSeriesBlueprint * * @typedef {Object} LineSeriesBlueprintSpecific * @property {"Line"} [type] * @property {Color} [color] * @property {DeepPartial} [options] * @property {Accessor} [data] * @typedef {BaseSeriesBlueprint & LineSeriesBlueprintSpecific} LineSeriesBlueprint * * @typedef {BaselineSeriesBlueprint | CandlestickSeriesBlueprint | LineSeriesBlueprint} AnySeriesBlueprint * * @typedef {AnySeriesBlueprint & { key: ChartableVecId }} AnyFetchedSeriesBlueprint * * @typedef {Object} PartialOption * @property {string} name * * @typedef {Object} ProcessedOptionAddons * @property {string} id * @property {string} title * @property {string[]} path * * @typedef {Object} PartialChartOptionSpecific * @property {"chart"} [kind] * @property {string} title * @property {AnyFetchedSeriesBlueprint[]} [top] * @property {AnyFetchedSeriesBlueprint[]} [bottom] * * @typedef {PartialOption & PartialChartOptionSpecific} PartialChartOption * * @typedef {Object} ProcessedChartOptionAddons * @property {Record} top * @property {Record} bottom * * @typedef {Required> & ProcessedChartOptionAddons & ProcessedOptionAddons} ChartOption * * @typedef {Object} PartialTableOptionSpecific * @property {"table"} kind * @property {string} title * * @typedef {PartialOption & PartialTableOptionSpecific} PartialTableOption * * @typedef {Required & ProcessedOptionAddons} TableOption * * @typedef {Object} PartialSimulationOptionSpecific * @property {"simulation"} kind * @property {string} title * * @typedef {PartialOption & PartialSimulationOptionSpecific} PartialSimulationOption * * @typedef {Required & ProcessedOptionAddons} SimulationOption * * @typedef {Object} PartialUrlOptionSpecific * @property {"url"} [kind] * @property {() => string} url * @property {boolean} [qrcode] * * @typedef {PartialOption & PartialUrlOptionSpecific} PartialUrlOption * * @typedef {Required & ProcessedOptionAddons} UrlOption * * @typedef {PartialChartOption | PartialTableOption | PartialSimulationOption | PartialUrlOption} AnyPartialOption * * @typedef {ChartOption | TableOption | SimulationOption | UrlOption} Option * * @typedef {Object} PartialOptionsGroup * @property {string} name * @property {PartialOptionsTree} tree * * @typedef {Object} OptionsGroup * @property {string} id * @property {string} name * @property {OptionsTree} tree * * @typedef {(AnyPartialOption | PartialOptionsGroup)[]} PartialOptionsTree * * @typedef {(Option | OptionsGroup)[]} OptionsTree * */ /** * @param {Colors} colors * @returns {PartialOptionsTree} */ function createPartialOptions(colors) { /** * @template {string} S * @typedef {Extract} StartsWith */ /** * @template {string} S * @typedef {Extract} EndsWith */ /** * @template {string} K * @template {string} S * @typedef {K extends `${S}${infer Rest}` ? Rest : never} WithoutPrefix */ /** * @template {string} K * @template {string} S * @typedef {K extends `${infer Rest}${S}` ? Rest : never} WithoutSuffix */ /** * @typedef {"total-"} TotalPrefix * @typedef {StartsWith} TotalVecId * @typedef {WithoutPrefix} TotalVecIdBase * @typedef {"-sum"} SumSuffix * @typedef {EndsWith} VecIdSum * @typedef {WithoutSuffix} VecIdSumBase * @typedef {"-average"} AverageSuffix * @typedef {EndsWith} VecIdAverage * @typedef {WithoutSuffix} VecIdAverageBase * @typedef {"-median"} MedianSuffix * @typedef {EndsWith} VecIdMedian * @typedef {WithoutSuffix} VecIdMedianBase * @typedef {"-90p"} _90pSuffix * @typedef {EndsWith<_90pSuffix>} VecId90p * @typedef {WithoutSuffix} VecId90pBase * @typedef {"-75p"} _75pSuffix * @typedef {EndsWith<_75pSuffix>} VecId75p * @typedef {WithoutSuffix} VecId75pBase * @typedef {"-25p"} _25pSuffix * @typedef {EndsWith<_25pSuffix>} VecId25p * @typedef {WithoutSuffix} VecId25pBase * @typedef {"-10p"} _10pSuffix * @typedef {EndsWith<_10pSuffix>} VecId10p * @typedef {WithoutSuffix} VecId10pBase * @typedef {"-max"} MaxSuffix * @typedef {EndsWith} VecIdMax * @typedef {WithoutSuffix} VecIdMaxBase * @typedef {"-min"} MinSuffix * @typedef {EndsWith} VecIdMin * @typedef {WithoutSuffix} VecIdMinBase */ /** * @param {Object} args * @param {ChartableVecId} args.key * @param {string} args.name */ function createBaseSeries({ key, name }) { return { key, title: name }; } /** * @param {Object} args * @param {VecIdAverageBase} args.concat */ function createAverageSeries({ concat }) { return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({ key: `${concat}-average`, title: "Average", }); } /** * @param {Object} args * @param {VecIdSumBase & TotalVecIdBase} args.concat * @param {string} [args.name] */ function createSumTotalSeries({ concat, name }) { return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([ { key: `${concat}-sum`, title: name ? `${name} Sum` : "Sum", color: colors.bitcoin, }, { key: `total-${concat}`, title: name ? `Total ${name}` : "Total", color: colors.offBitcoin, defaultActive: false, }, ]); } /** * @param {Object} args * @param {VecIdMinBase & VecIdMaxBase & VecId90pBase & VecId75pBase & VecIdMedianBase & VecId25pBase & VecId10pBase} args.concat */ function createMinMaxPercentilesSeries({ concat }) { return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([ { key: `${concat}-max`, title: "Max", color: colors.pink, defaultActive: false, }, { key: `${concat}-min`, title: "Min", color: colors.green, defaultActive: false, }, { key: `${concat}-median`, title: "Median", color: colors.amber, defaultActive: false, }, { key: `${concat}-75p`, title: "75p", color: colors.red, defaultActive: false, }, { key: `${concat}-25p`, title: "25p", color: colors.yellow, defaultActive: false, }, { key: `${concat}-90p`, title: "90p", color: colors.rose, defaultActive: false, }, { key: `${concat}-10p`, title: "10p", color: colors.lime, defaultActive: false, }, ]); } /** * @param {VecIdAverageBase & VecIdSumBase & TotalVecIdBase & VecIdMinBase & VecIdMaxBase & VecId90pBase & VecId75pBase & VecIdMedianBase & VecId25pBase & VecId10pBase} key */ function createAverageSumTotalMinMaxPercentilesSeries(key) { return [ createAverageSeries({ concat: key }), ...createSumTotalSeries({ concat: key }), ...createMinMaxPercentilesSeries({ concat: key }), ]; } /** * @param {Object} args * @param {ChartableVecId & VecIdAverageBase & VecIdSumBase & TotalVecIdBase & VecIdMinBase & VecIdMaxBase & VecId90pBase & VecId75pBase & VecIdMedianBase & VecId25pBase & VecId10pBase} args.key * @param {string} args.name */ function createBaseAverageSumTotalMinMaxPercentilesSeries({ key, name }) { return [ createBaseSeries({ key, name, }), ...createAverageSumTotalMinMaxPercentilesSeries(key), ]; } return [ { name: "Charts", tree: [ { name: "Price", title: "Bitcoin Price", }, { name: "Block", tree: [ { name: "Count", title: "Block Count", bottom: [ createBaseSeries({ key: "block-count", name: "Count", }), ...createSumTotalSeries({ concat: "block-count" }), ], }, { name: "Interval", title: "Block Interval", bottom: [ createBaseSeries({ key: "interval", name: "Interval", }), createAverageSeries({ concat: "block-interval" }), ...createMinMaxPercentilesSeries({ concat: "block-interval", }), ], }, { name: "Size", title: "Block Size", bottom: [ createBaseSeries({ key: "total-size", name: "Size", }), ...createSumTotalSeries({ concat: "block-size" }), ], }, { name: "Weight", title: "Block Weight", bottom: [ createBaseSeries({ key: "weight", name: "Weight", }), ...createSumTotalSeries({ concat: "block-weight" }), ], }, { name: "Vbytes", title: "Block Virtual Bytes", bottom: [ createBaseSeries({ key: "vbytes", name: "Vbytes", }), ...createSumTotalSeries({ concat: "block-vbytes" }), ], }, ], }, { name: "Transaction", tree: [ { name: "Count", title: "Transaction Count", bottom: createBaseAverageSumTotalMinMaxPercentilesSeries({ key: "tx-count", name: "Count", }), }, { name: "Subsidy", title: "Subsidy", bottom: [ ...createBaseAverageSumTotalMinMaxPercentilesSeries({ key: "subsidy", name: "Subsidy", }), ...createBaseAverageSumTotalMinMaxPercentilesSeries({ key: "subsidy-in-btc", name: "Subsidy", }), ...createBaseAverageSumTotalMinMaxPercentilesSeries({ key: "subsidy-in-usd", name: "Subsidy", }), ], }, { name: "Coinbase", title: "Coinbase", bottom: [ ...createBaseAverageSumTotalMinMaxPercentilesSeries({ key: "coinbase", name: "Coinbase", }), ...createBaseAverageSumTotalMinMaxPercentilesSeries({ key: "coinbase-in-btc", name: "Coinbase", }), ...createBaseAverageSumTotalMinMaxPercentilesSeries({ key: "coinbase-in-usd", name: "Coinbase", }), ], }, { name: "Fee", title: "Transaction Fee", bottom: [ ...createAverageSumTotalMinMaxPercentilesSeries("fee"), ...createAverageSumTotalMinMaxPercentilesSeries("fee-in-btc"), ...createAverageSumTotalMinMaxPercentilesSeries("fee-in-usd"), ], }, { name: "Feerate", title: "Transaction Fee Rate", bottom: [ createAverageSeries({ concat: "feerate" }), ...createMinMaxPercentilesSeries({ concat: "feerate", }), ], }, { name: "Weight", title: "Transaction Weight", bottom: [ createAverageSeries({ concat: "tx-weight" }), ...createMinMaxPercentilesSeries({ concat: "tx-weight", }), ], }, { name: "vsize", title: "Transaction Virtual Size", bottom: [ createAverageSeries({ concat: "tx-vsize" }), ...createMinMaxPercentilesSeries({ concat: "tx-vsize", }), ], }, { name: "Versions", title: "Transaction Versions", bottom: [ // { // name: "1", // title: "Transaction V1 Count", // bottom: [ createBaseSeries({ key: "tx-v1", name: "v1 Count", }), ...createSumTotalSeries({ concat: "tx-v1", name: "v1" }), // ], // }, // { // name: "2", // title: "Transaction V2 Count", // bottom: [ createBaseSeries({ key: "tx-v2", name: "v2 Count", }), ...createSumTotalSeries({ concat: "tx-v2", name: "v2" }), // ], // }, // { // name: "3", // title: "Transaction V3 Count", // bottom: [ createBaseSeries({ key: "tx-v3", name: "v3 Count", }), ...createSumTotalSeries({ concat: "tx-v3", name: "v3" }), // ], // }, ], }, ], }, { name: "Input", tree: [ { name: "Count", title: "Transaction Input Count", bottom: [ createAverageSeries({ concat: "input-count" }), ...createSumTotalSeries({ concat: "input-count" }), ...createMinMaxPercentilesSeries({ concat: "input-count", }), ], }, { name: "Value", title: "Transaction Input Value", bottom: [ createAverageSeries({ concat: "input-value" }), ...createSumTotalSeries({ concat: "input-value" }), ], }, ], }, { name: "Output", tree: [ { name: "Count", title: "Transaction Output Count", bottom: [ createAverageSeries({ concat: "output-count" }), ...createSumTotalSeries({ concat: "output-count" }), ...createMinMaxPercentilesSeries({ concat: "output-count", }), ], }, { name: "Value", title: "Transaction Output Value", bottom: [ createAverageSeries({ concat: "output-value" }), ...createSumTotalSeries({ concat: "output-value" }), ], }, ], }, ], }, { kind: "table", title: "Table", name: "Table", }, { name: "Simulations", tree: [ { kind: "simulation", title: "Save In Bitcoin", name: "Save In Bitcoin", }, ], }, { name: "Social", tree: [ { name: "Github", url: () => "https://github.com/bitcoinresearchkit/brk", }, { name: "Nostr", url: () => "https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44", }, { name: "Discord", url: () => "https://discord.com/invite/HaR3wpH3nr", }, { name: "Bluesky", url: () => "https://bsky.app/profile/bitcoinresearchkit.org", }, { name: "x", url: () => "https://x.com/brkdotorg", }, ], }, { name: "Developers", tree: [ { name: "API", url: () => "/api", }, { name: "Source", url: () => "https://github.com/bitcoinresearchkit/brk", }, { name: "Status", url: () => "https://status.kibo.money/", }, { name: "Crates", url: () => "https://crates.io/crates/brk", }, ], }, { name: "Donations", tree: [ { name: "Bitcoin QR Code", qrcode: true, url: () => "bitcoin:bc1q098zsm89m7kgyze338vfejhpdt92ua9p3peuve", }, { name: "Lightning QR Code", qrcode: true, url: () => "lightning:lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkxmmww3jkuar8d35kgetj8yuq363hv4", }, { name: "Geyser", url: () => "https://geyser.fund/project/brk", }, { name: "OpenSats", url: () => "https://opensats.org/", }, ], }, { name: "Share", qrcode: true, url: () => window.location.href, }, ]; } /** * @param {Object} args * @param {Colors} args.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, }) { const LS_SELECTED_KEY = `selected-id`; const urlSelected = utils.url.pathnameToSelectedId(); const savedSelectedId = localStorage.getItem(LS_SELECTED_KEY); /** @type {Signal