mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-15 04:58:12 -07:00
global: MASSIVE snapshot
This commit is contained in:
+4794
-3765
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@bitcoinresearchkit/client",
|
||||
"version": "0.1.0-alpha.1",
|
||||
"description": "BRK JavaScript client",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"exports": {
|
||||
".": "./index.js"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"generated"
|
||||
],
|
||||
"keywords": [
|
||||
"brk",
|
||||
"bitcoin",
|
||||
"blockchain",
|
||||
"research"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/bitcoinresearchkit/brk.git",
|
||||
"directory": "modules/brk-client"
|
||||
},
|
||||
"homepage": "https://github.com/bitcoinresearchkit/brk/tree/main/modules/brk-client",
|
||||
"bugs": {
|
||||
"url": "https://github.com/bitcoinresearchkit/brk/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/**
|
||||
* @import { Signal, Signals } from "../brk-signals/index";
|
||||
* @import { MetricNode } from "../brk-client/index";
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} Resource
|
||||
* @property {Signal<T | null>} data
|
||||
* @property {Signal<boolean>} loading
|
||||
* @property {Signal<Error | null>} error
|
||||
* @property {(...args: any[]) => Promise<T | null>} fetch
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} RangeState
|
||||
* @property {Signal<T[] | null>} data
|
||||
* @property {Signal<boolean>} loading
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} MetricResource
|
||||
* @property {string} path
|
||||
* @property {(from?: number, to?: number) => RangeState<T>} range
|
||||
* @property {(from?: number, to?: number) => Promise<T[] | null>} fetch
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {ReturnType<typeof createResources>} Resources
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Signals} signals
|
||||
*/
|
||||
export function createResources(signals) {
|
||||
const owner = signals.getOwner();
|
||||
|
||||
/**
|
||||
* Create a generic reactive resource wrapper for any async fetcher
|
||||
* @template T
|
||||
* @template {any[]} Args
|
||||
* @param {(...args: Args) => Promise<T>} fetcher
|
||||
* @returns {Resource<T>}
|
||||
*/
|
||||
function createResource(fetcher) {
|
||||
return signals.runWithOwner(owner, () => {
|
||||
const data = signals.createSignal(/** @type {T | null} */ (null));
|
||||
const loading = signals.createSignal(false);
|
||||
const error = signals.createSignal(/** @type {Error | null} */ (null));
|
||||
|
||||
return {
|
||||
data,
|
||||
loading,
|
||||
error,
|
||||
/**
|
||||
* @param {Args} args
|
||||
*/
|
||||
async fetch(...args) {
|
||||
loading.set(true);
|
||||
error.set(null);
|
||||
try {
|
||||
const result = await fetcher(...args);
|
||||
data.set(result);
|
||||
return result;
|
||||
} catch (e) {
|
||||
error.set(e instanceof Error ? e : new Error(String(e)));
|
||||
return null;
|
||||
} finally {
|
||||
loading.set(false);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reactive resource wrapper for a MetricNode with multi-range support
|
||||
* @template T
|
||||
* @param {MetricNode<T>} node
|
||||
* @returns {MetricResource<T>}
|
||||
*/
|
||||
function useMetricNode(node) {
|
||||
return signals.runWithOwner(owner, () => {
|
||||
/** @type {Map<string, RangeState<T>>} */
|
||||
const ranges = new Map();
|
||||
|
||||
/**
|
||||
* Get or create range state
|
||||
* @param {number} [from=-10000]
|
||||
* @param {number} [to]
|
||||
*/
|
||||
function range(from = -10000, to) {
|
||||
const key = `${from}-${to ?? ""}`;
|
||||
if (!ranges.has(key)) {
|
||||
ranges.set(key, {
|
||||
data: signals.createSignal(/** @type {T[] | null} */ (null)),
|
||||
loading: signals.createSignal(false),
|
||||
});
|
||||
}
|
||||
return /** @type {RangeState<T>} */ (ranges.get(key));
|
||||
}
|
||||
|
||||
return {
|
||||
path: node._path,
|
||||
range,
|
||||
/**
|
||||
* Fetch data for a range
|
||||
* @param {number} [from=-10000]
|
||||
* @param {number} [to]
|
||||
*/
|
||||
async fetch(from = -10000, to) {
|
||||
const r = range(from, to);
|
||||
r.loading.set(true);
|
||||
try {
|
||||
const result = await node.getRange(from, to, r.data.set);
|
||||
return result;
|
||||
} finally {
|
||||
r.loading.set(false);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return { createResource, useMetricNode };
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"checkJs": true,
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"outDir": "/tmp/brk",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
/**
|
||||
* @import { SignalOptions } from "../solidjs-signals/0.6.3/dist/types/core/core"
|
||||
* @import { getOwner as GetOwner, onCleanup as OnCleanup } from "../solidjs-signals/0.6.3/dist/types/core/owner"
|
||||
* @import { createSignal as CreateSignal, createEffect as CreateEffect, createMemo as CreateMemo, createRoot as CreateRoot, runWithOwner as RunWithOwner, Setter } from "../solidjs-signals/0.6.3/dist/types/signals";
|
||||
*/
|
||||
|
||||
// test
|
||||
// wkopwfk
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {() => T} Accessor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Accessor<T> & { set: Setter<T>; reset: VoidFunction }} Signal
|
||||
*/
|
||||
|
||||
import {
|
||||
createSignal,
|
||||
createEffect,
|
||||
getOwner,
|
||||
createMemo,
|
||||
createRoot,
|
||||
runWithOwner,
|
||||
onCleanup,
|
||||
} from "../solidjs-signals/0.6.3/dist/prod.js";
|
||||
|
||||
let effectCount = 0;
|
||||
|
||||
const signals = {
|
||||
createSolidSignal: /** @type {typeof CreateSignal} */ (createSignal),
|
||||
createSolidEffect: /** @type {typeof CreateEffect} */ (createEffect),
|
||||
createEffect: /** @type {typeof CreateEffect} */ (
|
||||
// @ts-ignore
|
||||
(compute, effect) => {
|
||||
let dispose = /** @type {VoidFunction | null} */ (null);
|
||||
|
||||
if (getOwner() === null) {
|
||||
throw Error("No owner");
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
if (dispose) {
|
||||
dispose();
|
||||
dispose = null;
|
||||
// console.log("effectCount = ", --effectCount);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
createEffect(compute, (v, oldV) => {
|
||||
// console.log("effectCount = ", ++effectCount);
|
||||
cleanup();
|
||||
signals.createRoot((_dispose) => {
|
||||
dispose = _dispose;
|
||||
return effect(v, oldV);
|
||||
});
|
||||
signals.onCleanup(cleanup);
|
||||
});
|
||||
signals.onCleanup(cleanup);
|
||||
}
|
||||
),
|
||||
createMemo: /** @type {typeof CreateMemo} */ (createMemo),
|
||||
createRoot: /** @type {typeof CreateRoot} */ (createRoot),
|
||||
getOwner: /** @type {typeof GetOwner} */ (getOwner),
|
||||
runWithOwner: /** @type {typeof RunWithOwner} */ (runWithOwner),
|
||||
onCleanup: /** @type {typeof OnCleanup} */ (onCleanup),
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} initialValue
|
||||
* @param {SignalOptions<T> & {save?: {keyPrefix: string | Accessor<string>; key: string; serialize: (v: T) => string; deserialize: (v: string) => T; serializeParam?: boolean; saveDefaultValue?: boolean}}} [options]
|
||||
* @returns {Signal<T>}
|
||||
*/
|
||||
createSignal(initialValue, options) {
|
||||
const [get, set] = this.createSolidSignal(
|
||||
/** @type {any} */ (initialValue),
|
||||
options,
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
get.set = set;
|
||||
|
||||
// @ts-ignore
|
||||
get.reset = () => set(initialValue);
|
||||
|
||||
if (options?.save) {
|
||||
const save = options.save;
|
||||
|
||||
const paramKey = save.key;
|
||||
const storageKey = this.createMemo(
|
||||
() =>
|
||||
`${
|
||||
typeof save.keyPrefix === "string"
|
||||
? save.keyPrefix
|
||||
: save.keyPrefix()
|
||||
}-${paramKey}`,
|
||||
);
|
||||
|
||||
/** @type { ((this: Window, ev: PopStateEvent) => any) | undefined} */
|
||||
let popstateCallback;
|
||||
|
||||
let serialized = /** @type {string | null} */ (null);
|
||||
if (options.save.serializeParam !== false) {
|
||||
serialized = new URLSearchParams(window.location.search).get(paramKey);
|
||||
|
||||
// popstateCallback =
|
||||
// /** @type {(this: Window, ev: PopStateEvent) => any} */ (
|
||||
// (_) => {
|
||||
// serialized = new URLSearchParams(window.location.search).get(
|
||||
// paramKey,
|
||||
// );
|
||||
// set(() =>
|
||||
// serialized ? save.deserialize(serialized) : initialValue,
|
||||
// );
|
||||
// }
|
||||
// );
|
||||
// if (!popstateCallback) throw "Unreachable";
|
||||
// window.addEventListener("popstate", popstateCallback);
|
||||
// signals.onCleanup(() => {
|
||||
// if (popstateCallback)
|
||||
// window.removeEventListener("popstate", popstateCallback);
|
||||
// });
|
||||
}
|
||||
if (serialized === null) {
|
||||
try {
|
||||
serialized = localStorage.getItem(storageKey());
|
||||
} catch (_) {}
|
||||
}
|
||||
if (serialized) {
|
||||
set(() => (serialized ? save.deserialize(serialized) : initialValue));
|
||||
}
|
||||
|
||||
let firstRun1 = true;
|
||||
this.createEffect(storageKey, (storageKey) => {
|
||||
if (!firstRun1) {
|
||||
try {
|
||||
serialized = localStorage.getItem(storageKey);
|
||||
set(() =>
|
||||
serialized ? save.deserialize(serialized) : initialValue,
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
firstRun1 = false;
|
||||
});
|
||||
|
||||
let firstRun2 = true;
|
||||
this.createEffect(get, (value) => {
|
||||
if (!save) return;
|
||||
|
||||
if (!firstRun2) {
|
||||
try {
|
||||
if (
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
(initialValue === undefined ||
|
||||
initialValue === null ||
|
||||
save.saveDefaultValue ||
|
||||
save.serialize(value) !== save.serialize(initialValue))
|
||||
) {
|
||||
localStorage.setItem(storageKey(), save.serialize(value));
|
||||
} else {
|
||||
localStorage.removeItem(storageKey());
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
if (
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
(initialValue === undefined ||
|
||||
initialValue === null ||
|
||||
save.saveDefaultValue ||
|
||||
save.serialize(value) !== save.serialize(initialValue))
|
||||
) {
|
||||
writeParam(paramKey, save.serialize(value));
|
||||
} else {
|
||||
removeParam(paramKey);
|
||||
}
|
||||
|
||||
firstRun2 = false;
|
||||
});
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return get;
|
||||
},
|
||||
};
|
||||
/** @typedef {typeof signals} Signals */
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string | undefined} value
|
||||
*/
|
||||
function writeParam(key, value) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
if (value !== undefined) {
|
||||
urlParams.set(key, String(value));
|
||||
} else {
|
||||
urlParams.delete(key);
|
||||
}
|
||||
|
||||
try {
|
||||
window.history.replaceState(
|
||||
null,
|
||||
"",
|
||||
`${window.location.pathname}?${urlParams.toString()}`,
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
*/
|
||||
function removeParam(key) {
|
||||
writeParam(key, undefined);
|
||||
}
|
||||
|
||||
export default signals;
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"checkJs": true,
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"outDir": "/tmp/brk",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user