@lodestar/utils 1.35.0-dev.f80d2d52da → 1.35.0-dev.fcf8d024ea

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/lib/assert.d.ts.map +1 -0
  2. package/lib/assert.js +1 -1
  3. package/lib/assert.js.map +1 -1
  4. package/lib/base64.d.ts.map +1 -0
  5. package/lib/bytes/browser.d.ts.map +1 -0
  6. package/lib/bytes/index.d.ts.map +1 -0
  7. package/lib/bytes/nodejs.d.ts.map +1 -0
  8. package/lib/bytes.d.ts.map +1 -0
  9. package/lib/command.d.ts.map +1 -0
  10. package/lib/diff.d.ts.map +1 -0
  11. package/lib/err.d.ts.map +1 -0
  12. package/lib/errors.d.ts.map +1 -0
  13. package/lib/errors.js +1 -0
  14. package/lib/errors.js.map +1 -1
  15. package/lib/ethConversion.d.ts.map +1 -0
  16. package/lib/fetch.d.ts.map +1 -0
  17. package/lib/fetch.js +3 -0
  18. package/lib/fetch.js.map +1 -1
  19. package/lib/format.d.ts.map +1 -0
  20. package/lib/index.d.ts +8 -8
  21. package/lib/index.d.ts.map +1 -0
  22. package/lib/index.js +6 -6
  23. package/lib/index.js.map +1 -1
  24. package/lib/iterator.d.ts.map +1 -0
  25. package/lib/logger.d.ts.map +1 -0
  26. package/lib/map.d.ts.map +1 -0
  27. package/lib/map.js +6 -7
  28. package/lib/map.js.map +1 -1
  29. package/lib/math.d.ts.map +1 -0
  30. package/lib/metrics.d.ts.map +1 -0
  31. package/lib/notNullish.d.ts.map +1 -0
  32. package/lib/objects.d.ts.map +1 -0
  33. package/lib/promise.d.ts.map +1 -0
  34. package/lib/retry.d.ts.map +1 -0
  35. package/lib/sleep.d.ts.map +1 -0
  36. package/lib/sort.d.ts.map +1 -0
  37. package/lib/timeout.d.ts.map +1 -0
  38. package/lib/types.d.ts +6 -4
  39. package/lib/types.d.ts.map +1 -0
  40. package/lib/types.js.map +1 -1
  41. package/lib/url.d.ts.map +1 -0
  42. package/lib/verifyMerkleBranch.d.ts.map +1 -0
  43. package/lib/waitFor.d.ts.map +1 -0
  44. package/lib/yaml/index.d.ts.map +1 -0
  45. package/lib/yaml/int.d.ts.map +1 -0
  46. package/lib/yaml/schema.d.ts.map +1 -0
  47. package/lib/yaml/schema.js.map +1 -1
  48. package/package.json +11 -8
  49. package/src/assert.ts +86 -0
  50. package/src/base64.ts +9 -0
  51. package/src/bytes/browser.ts +123 -0
  52. package/src/bytes/index.ts +29 -0
  53. package/src/bytes/nodejs.ts +63 -0
  54. package/src/bytes.ts +84 -0
  55. package/src/command.ts +74 -0
  56. package/src/diff.ts +234 -0
  57. package/src/err.ts +105 -0
  58. package/src/errors.ts +73 -0
  59. package/src/ethConversion.ts +12 -0
  60. package/src/fetch.ts +188 -0
  61. package/src/format.ts +119 -0
  62. package/src/index.ts +28 -0
  63. package/src/iterator.ts +10 -0
  64. package/src/logger.ts +20 -0
  65. package/src/map.ts +108 -0
  66. package/src/math.ts +55 -0
  67. package/src/metrics.ts +73 -0
  68. package/src/notNullish.ts +11 -0
  69. package/src/objects.ts +102 -0
  70. package/src/promise.ts +163 -0
  71. package/src/retry.ts +75 -0
  72. package/src/sleep.ts +32 -0
  73. package/src/sort.ts +9 -0
  74. package/src/timeout.ts +27 -0
  75. package/src/types.ts +48 -0
  76. package/src/url.ts +29 -0
  77. package/src/verifyMerkleBranch.ts +27 -0
  78. package/src/waitFor.ts +87 -0
  79. package/src/yaml/index.ts +12 -0
  80. package/src/yaml/int.ts +190 -0
  81. package/src/yaml/schema.ts +8 -0
package/src/math.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Return the min number between two big numbers.
3
+ */
4
+ export function bigIntMin(a: bigint, b: bigint): bigint {
5
+ return a < b ? a : b;
6
+ }
7
+
8
+ /**
9
+ * Return the max number between two big numbers.
10
+ */
11
+ export function bigIntMax(a: bigint, b: bigint): bigint {
12
+ return a > b ? a : b;
13
+ }
14
+
15
+ export function intDiv(dividend: number, divisor: number): number {
16
+ return Math.floor(dividend / divisor);
17
+ }
18
+
19
+ /**
20
+ * Calculate the largest integer k such that k**2 <= n.
21
+ */
22
+ export function intSqrt(n: number): number {
23
+ let x = n;
24
+ let y = intDiv(x + 1, 2);
25
+ while (y < x) {
26
+ x = y;
27
+ y = intDiv(x + intDiv(n, x), 2);
28
+ }
29
+ return x;
30
+ }
31
+
32
+ export function bigIntSqrt(n: bigint): bigint {
33
+ let x = n;
34
+ let y = (x + BigInt(1)) / BigInt(2);
35
+ while (y < x) {
36
+ x = y;
37
+ y = (x + n / x) / BigInt(2);
38
+ }
39
+ return x;
40
+ }
41
+
42
+ /**
43
+ * Regenerates a random integer between min (included) and max (excluded).
44
+ */
45
+ export function randBetween(min: number, max: number): number {
46
+ return Math.floor(Math.random() * (max - min)) + min;
47
+ }
48
+
49
+ /**
50
+ * Wraps randBetween and returns a bigNumber.
51
+ * @returns {bigint}
52
+ */
53
+ export function randBetweenBigInt(min: number, max: number): bigint {
54
+ return BigInt(randBetween(min, max));
55
+ }
package/src/metrics.ts ADDED
@@ -0,0 +1,73 @@
1
+ import {NonEmptyArray} from "./types.js";
2
+
3
+ export type NoLabels = Record<string, never>;
4
+ export type LabelsGeneric = Record<string, string | number>;
5
+ export type LabelKeys<Labels extends LabelsGeneric> = Extract<keyof Labels, string>;
6
+ export type CollectFn<Labels extends LabelsGeneric> = (metric: Gauge<Labels>) => void;
7
+
8
+ export interface Gauge<Labels extends LabelsGeneric = NoLabels> {
9
+ inc: NoLabels extends Labels ? (value?: number) => void : (labels: Labels, value?: number) => void;
10
+ dec: NoLabels extends Labels ? (value?: number) => void : (labels: Labels, value?: number) => void;
11
+ set: NoLabels extends Labels ? (value: number) => void : (labels: Labels, value: number) => void;
12
+
13
+ collect?(): void;
14
+ }
15
+
16
+ export interface GaugeExtra<Labels extends LabelsGeneric = NoLabels> extends Omit<Gauge<Labels>, "collect"> {
17
+ addCollect(collectFn: CollectFn<Labels>): void;
18
+ }
19
+
20
+ export interface Histogram<Labels extends LabelsGeneric = NoLabels> {
21
+ startTimer(): NoLabels extends Labels ? () => number : (labels: Labels) => number;
22
+ startTimer<L extends Partial<Labels>>(
23
+ labels?: NoLabels extends Labels ? never : L
24
+ ): keyof Omit<Labels, keyof L> extends never ? () => number : (labels: Omit<Labels, keyof L>) => number;
25
+
26
+ observe: NoLabels extends Labels ? (value: number) => void : (labels: Labels, value: number) => void;
27
+
28
+ reset(): void;
29
+ }
30
+
31
+ export interface AvgMinMax<Labels extends LabelsGeneric = NoLabels> {
32
+ addGetValuesFn(getValuesFn: () => number[]): void;
33
+
34
+ set: NoLabels extends Labels ? (values: number[]) => void : (labels: Labels, values: number[]) => void;
35
+ }
36
+
37
+ export interface Counter<Labels extends LabelsGeneric = NoLabels> {
38
+ inc: NoLabels extends Labels ? (value?: number) => void : (labels: Labels, value?: number) => void;
39
+ }
40
+
41
+ export type GaugeConfig<Labels extends LabelsGeneric> = {
42
+ name: string;
43
+ help: string;
44
+ } & (NoLabels extends Labels ? {labelNames?: never} : {labelNames: NonEmptyArray<LabelKeys<Labels>>});
45
+
46
+ export type HistogramConfig<Labels extends LabelsGeneric> = GaugeConfig<Labels> & {
47
+ buckets: number[];
48
+ };
49
+
50
+ export type AvgMinMaxConfig<Labels extends LabelsGeneric> = GaugeConfig<Labels>;
51
+
52
+ export type CounterConfig<Labels extends LabelsGeneric> = GaugeConfig<Labels>;
53
+
54
+ export type StaticConfig<Labels extends LabelsGeneric> = {
55
+ name: GaugeConfig<Labels>["name"];
56
+ help: GaugeConfig<Labels>["help"];
57
+ value: Record<LabelKeys<Labels>, string>;
58
+ };
59
+
60
+ export interface MetricsRegister {
61
+ gauge<Labels extends LabelsGeneric = NoLabels>(config: GaugeConfig<Labels>): Gauge<Labels>;
62
+ histogram<Labels extends LabelsGeneric = NoLabels>(config: HistogramConfig<Labels>): Histogram<Labels>;
63
+ counter<Labels extends LabelsGeneric = NoLabels>(config: CounterConfig<Labels>): Counter<Labels>;
64
+ }
65
+
66
+ export interface MetricsRegisterExtra extends MetricsRegister {
67
+ gauge<Labels extends LabelsGeneric = NoLabels>(config: GaugeConfig<Labels>): GaugeExtra<Labels>;
68
+ }
69
+
70
+ export interface MetricsRegisterCustom extends MetricsRegisterExtra {
71
+ avgMinMax<Labels extends LabelsGeneric = NoLabels>(config: AvgMinMaxConfig<Labels>): AvgMinMax<Labels>;
72
+ static<Labels extends LabelsGeneric = NoLabels>(config: StaticConfig<Labels>): void;
73
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Type-safe helper to filter out nullist values from an array
3
+ * ```js
4
+ * const array: (string | null)[] = ['foo', null];
5
+ * const filteredArray: string[] = array.filter(notEmpty);
6
+ * ```
7
+ * @param value
8
+ */
9
+ export function notNullish<TValue>(value: TValue | null | undefined): value is TValue {
10
+ return value !== null && value !== undefined;
11
+ }
package/src/objects.ts ADDED
@@ -0,0 +1,102 @@
1
+ import Case from "case";
2
+
3
+ export type KeyCase =
4
+ | "snake"
5
+ | "constant"
6
+ | "camel"
7
+ | "param"
8
+ | "header"
9
+ | "pascal" //Same as squish
10
+ | "dot"
11
+ | "notransform";
12
+
13
+ export function toExpectedCase(
14
+ value: string,
15
+ expectedCase: KeyCase = "camel",
16
+ customCasingMap?: Record<string, string>
17
+ ): string {
18
+ if (expectedCase === "notransform") return value;
19
+ if (customCasingMap?.[value]) return customCasingMap[value];
20
+ switch (expectedCase) {
21
+ case "param":
22
+ return Case.kebab(value);
23
+ case "dot":
24
+ return Case.lower(value, ".", true);
25
+ default:
26
+ return Case[expectedCase](value);
27
+ }
28
+ }
29
+
30
+ function isObjectObject(val: unknown): val is object {
31
+ return val != null && typeof val === "object" && Array.isArray(val) === false;
32
+ }
33
+
34
+ export function isPlainObject(o: unknown): o is object {
35
+ if (isObjectObject(o) === false) return false;
36
+
37
+ // If has modified constructor
38
+ const ctor = (o as Record<string, unknown>).constructor;
39
+ if (typeof ctor !== "function") return false;
40
+
41
+ // If has modified prototype
42
+ const prot = ctor.prototype;
43
+ if (isObjectObject(prot) === false) return false;
44
+
45
+ // If constructor does not have an Object-specific method
46
+ if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
47
+ return false;
48
+ }
49
+
50
+ // Most likely a plain Object
51
+ return true;
52
+ }
53
+
54
+ export function isEmptyObject(value: unknown): boolean {
55
+ return isObjectObject(value) && Object.keys(value).length === 0;
56
+ }
57
+
58
+ /**
59
+ * Creates an object with the same keys as object and values generated by running each own enumerable
60
+ * string keyed property of object thru iteratee.
61
+ *
62
+ * Inspired on lodash.mapValues, see https://lodash.com/docs/4.17.15#mapValues
63
+ */
64
+ // biome-ignore lint/suspicious/noExplicitAny: We need to use `any` type here
65
+ export function mapValues<T extends {[K: string]: any}, R>(
66
+ obj: T,
67
+ iteratee: (value: T[keyof T], key: keyof T) => R
68
+ ): {[K in keyof T]: R} {
69
+ const output = {} as {[K in keyof T]: R};
70
+ for (const [key, value] of Object.entries(obj)) {
71
+ output[key as keyof T] = iteratee(value, key);
72
+ }
73
+ return output;
74
+ }
75
+
76
+ export function objectToExpectedCase<T extends Record<string, unknown> | Record<string, unknown>[] | unknown[]>(
77
+ obj: T,
78
+ expectedCase: "snake" | "constant" | "camel" | "param" | "header" | "pascal" | "dot" | "notransform" = "camel"
79
+ ): T {
80
+ if (Array.isArray(obj)) {
81
+ const newArr: unknown[] = [];
82
+ for (let i = 0; i < obj.length; i++) {
83
+ newArr[i] = objectToExpectedCase(obj[i] as T, expectedCase);
84
+ }
85
+ return newArr as unknown as T;
86
+ }
87
+
88
+ if (Object(obj) === obj) {
89
+ const newObj: Record<string, unknown> = {};
90
+ for (const name of Object.getOwnPropertyNames(obj)) {
91
+ const newName = toExpectedCase(name, expectedCase);
92
+ if (newName !== name && Object.prototype.hasOwnProperty.call(obj, newName)) {
93
+ throw new Error(`object already has a ${newName} property`);
94
+ }
95
+
96
+ newObj[newName] = objectToExpectedCase(obj[name] as Record<string, unknown>, expectedCase);
97
+ }
98
+ return newObj as T;
99
+ }
100
+
101
+ return obj;
102
+ }
package/src/promise.ts ADDED
@@ -0,0 +1,163 @@
1
+ import {ErrorAborted, TimeoutError} from "./errors.js";
2
+ import {sleep} from "./sleep.js";
3
+ import {ArrayToTuple, NonEmptyArray} from "./types.js";
4
+
5
+ /**
6
+ * While promise t is not finished, call function `fn` per `interval`
7
+ */
8
+ export async function callFnWhenAwait<T>(
9
+ p: Promise<T>,
10
+ fn: () => void,
11
+ interval: number,
12
+ signal?: AbortSignal
13
+ ): Promise<T> {
14
+ let done = false;
15
+ const logFn = async (): Promise<void> => {
16
+ while (!done) {
17
+ await sleep(interval, signal);
18
+ if (!done) fn();
19
+ }
20
+ };
21
+
22
+ const t = await Promise.race([p, logFn()]).finally(() => {
23
+ done = true;
24
+ });
25
+
26
+ return t as T;
27
+ }
28
+
29
+ /**
30
+ * Create a deferred promise
31
+ */
32
+ export function defer<T>() {
33
+ let resolve!: (v: T) => void;
34
+ let reject!: (e: unknown) => void;
35
+ const promise = new Promise<T>((res, rej) => {
36
+ resolve = res;
37
+ reject = rej;
38
+ });
39
+ return {promise, resolve, reject};
40
+ }
41
+
42
+ export type PromiseResult<T> = {
43
+ promise: Promise<T>;
44
+ } & (
45
+ | {
46
+ status: "pending";
47
+ }
48
+ | {
49
+ status: "fulfilled";
50
+ value: T;
51
+ durationMs: number;
52
+ }
53
+ | {
54
+ status: "rejected";
55
+ reason: Error;
56
+ durationMs: number;
57
+ }
58
+ );
59
+ export type PromiseFulfilledResult<T> = PromiseResult<T> & {status: "fulfilled"};
60
+ export type PromiseRejectedResult<T> = PromiseResult<T> & {status: "rejected"};
61
+
62
+ /**
63
+ * Wrap a promise to an object to track the status and value of the promise
64
+ */
65
+ export function wrapPromise<T>(promise: PromiseLike<T>): PromiseResult<T> {
66
+ const startedAt = Date.now();
67
+
68
+ const result = {
69
+ promise: promise.then(
70
+ (value) => {
71
+ result.status = "fulfilled";
72
+ (result as PromiseFulfilledResult<T>).value = value;
73
+ (result as PromiseFulfilledResult<T>).durationMs = Date.now() - startedAt;
74
+ return value;
75
+ },
76
+ (reason: unknown) => {
77
+ result.status = "rejected";
78
+ (result as PromiseRejectedResult<T>).reason = reason as Error;
79
+ (result as PromiseRejectedResult<T>).durationMs = Date.now() - startedAt;
80
+ throw reason;
81
+ }
82
+ ),
83
+ status: "pending",
84
+ } as PromiseResult<T>;
85
+
86
+ return result;
87
+ }
88
+
89
+ type ReturnPromiseWithTuple<Tuple extends NonEmptyArray<PromiseLike<unknown>>> = {
90
+ [Index in keyof ArrayToTuple<Tuple>]: PromiseResult<Awaited<Tuple[Index]>>;
91
+ };
92
+
93
+ /**
94
+ * Two phased approach for resolving promises:
95
+ * - first wait `resolveTimeoutMs` or until all promises settle
96
+ * - then wait `raceTimeoutMs - resolveTimeoutMs` or until at least a single promise resolves
97
+ *
98
+ * Returns a list of promise results, see `PromiseResult`
99
+ */
100
+ export async function resolveOrRacePromises<T extends NonEmptyArray<PromiseLike<unknown>>>(
101
+ promises: T,
102
+ {
103
+ resolveTimeoutMs,
104
+ raceTimeoutMs,
105
+ signal,
106
+ }: {
107
+ resolveTimeoutMs: number;
108
+ raceTimeoutMs: number;
109
+ signal?: AbortSignal;
110
+ }
111
+ ): Promise<ReturnPromiseWithTuple<T>> | never {
112
+ if (raceTimeoutMs <= resolveTimeoutMs) {
113
+ throw new Error("Race time must be greater than resolve time");
114
+ }
115
+ const resolveTimeoutError = new TimeoutError(
116
+ `Given promises can't be resolved within resolveTimeoutMs=${resolveTimeoutMs}`
117
+ );
118
+ const raceTimeoutError = new TimeoutError(
119
+ `Not a any single promise be resolved in given raceTimeoutMs=${raceTimeoutMs}`
120
+ );
121
+
122
+ const promiseResults = promises.map((p) => wrapPromise(p)) as ReturnPromiseWithTuple<T>;
123
+ // We intentionally want an array of promises here
124
+ promises = (promiseResults as PromiseResult<T>[]).map((p) => p.promise) as unknown as T;
125
+
126
+ try {
127
+ await Promise.race([
128
+ Promise.allSettled(promises),
129
+ sleep(resolveTimeoutMs, signal).then(() => {
130
+ throw resolveTimeoutError;
131
+ }),
132
+ ]);
133
+
134
+ return promiseResults;
135
+ } catch (err) {
136
+ if (err instanceof ErrorAborted) {
137
+ return promiseResults;
138
+ }
139
+ if (err !== resolveTimeoutError) {
140
+ throw err;
141
+ }
142
+ }
143
+
144
+ try {
145
+ await Promise.race([
146
+ Promise.any(promises),
147
+ sleep(raceTimeoutMs - resolveTimeoutMs, signal).then(() => {
148
+ throw raceTimeoutError;
149
+ }),
150
+ ]);
151
+
152
+ return promiseResults;
153
+ } catch (err) {
154
+ if (err instanceof ErrorAborted) {
155
+ return promiseResults;
156
+ }
157
+ if (err !== raceTimeoutError && !(err instanceof AggregateError)) {
158
+ throw err;
159
+ }
160
+ }
161
+
162
+ return promiseResults;
163
+ }
package/src/retry.ts ADDED
@@ -0,0 +1,75 @@
1
+ import {ErrorAborted} from "./errors.js";
2
+ import {sleep} from "./sleep.js";
3
+
4
+ export type RetryOptions = {
5
+ /**
6
+ * The maximum amount of times to retry the operation. Default is 5
7
+ */
8
+ retries?: number;
9
+ /**
10
+ * An optional Function that is invoked after the provided callback throws.
11
+ * It expects a boolean to know if it should retry or not.
12
+ * Useful to make retrying conditional on the type of error thrown.
13
+ */
14
+ shouldRetry?: (lastError: Error) => boolean;
15
+ /**
16
+ * An optional Function that is invoked right before a retry is performed.
17
+ * It's passed the Error that triggered it and a number identifying the attempt.
18
+ * Useful to track number of retries and errors in logs or metrics.
19
+ */
20
+ onRetry?: (lastError: Error, attempt: number) => unknown;
21
+ /**
22
+ * Milliseconds to wait before retrying again
23
+ */
24
+ retryDelay?: number;
25
+ /**
26
+ * Abort signal to stop retrying
27
+ */
28
+ signal?: AbortSignal;
29
+ };
30
+
31
+ /**
32
+ * Retry a given function on error.
33
+ * @param fn Async callback to retry. Invoked with 1 parameter
34
+ * A Number identifying the attempt. The absolute first attempt (before any retries) is 1
35
+ * @param opts
36
+ */
37
+ export async function retry<A>(fn: (attempt: number) => A | Promise<A>, opts?: RetryOptions): Promise<A> {
38
+ const maxRetries = opts?.retries ?? 5;
39
+ // Number of retries + the initial attempt
40
+ const maxAttempts = maxRetries + 1;
41
+ const shouldRetry = opts?.shouldRetry;
42
+ const onRetry = opts?.onRetry;
43
+
44
+ let lastError: Error = Error("RetryError");
45
+ for (let i = 1; i <= maxAttempts; i++) {
46
+ // If not the first attempt
47
+ if (i > 1) {
48
+ if (opts?.signal?.aborted) {
49
+ throw new ErrorAborted("retry");
50
+ }
51
+ // Invoke right before retrying
52
+ onRetry?.(lastError, i);
53
+ }
54
+
55
+ try {
56
+ return await fn(i);
57
+ } catch (e) {
58
+ lastError = e as Error;
59
+
60
+ if (i === maxAttempts) {
61
+ // Reached maximum number of attempts, there's no need to check if we should retry
62
+ break;
63
+ }
64
+
65
+ if (shouldRetry && !shouldRetry(lastError)) {
66
+ break;
67
+ }
68
+
69
+ if (opts?.retryDelay !== undefined) {
70
+ await sleep(opts?.retryDelay, opts?.signal);
71
+ }
72
+ }
73
+ }
74
+ throw lastError;
75
+ }
package/src/sleep.ts ADDED
@@ -0,0 +1,32 @@
1
+ import {ErrorAborted} from "./errors.js";
2
+
3
+ /**
4
+ * Abortable sleep function. Cleans everything on all cases preventing leaks
5
+ * On abort throws ErrorAborted
6
+ */
7
+ export async function sleep(ms: number, signal?: AbortSignal): Promise<void> {
8
+ if (ms < 0) {
9
+ return;
10
+ }
11
+
12
+ return new Promise((resolve, reject) => {
13
+ if (signal?.aborted) return reject(new ErrorAborted());
14
+
15
+ let onDone: () => void = () => {};
16
+
17
+ const timeout = setTimeout(() => {
18
+ onDone();
19
+ resolve();
20
+ }, ms);
21
+ const onAbort = (): void => {
22
+ onDone();
23
+ reject(new ErrorAborted());
24
+ };
25
+ if (signal) signal.addEventListener("abort", onAbort);
26
+
27
+ onDone = () => {
28
+ clearTimeout(timeout);
29
+ if (signal) signal.removeEventListener("abort", onAbort);
30
+ };
31
+ });
32
+ }
package/src/sort.ts ADDED
@@ -0,0 +1,9 @@
1
+ export function isSorted(indices: number[]): boolean {
2
+ for (let i = 0, prevIndex = -1; i < indices.length; i++) {
3
+ if (indices[i] <= prevIndex) {
4
+ return false;
5
+ }
6
+ prevIndex = indices[i];
7
+ }
8
+ return true;
9
+ }
package/src/timeout.ts ADDED
@@ -0,0 +1,27 @@
1
+ import {anySignal} from "any-signal";
2
+ import {ErrorAborted, TimeoutError} from "./errors.js";
3
+ import {sleep} from "./sleep.js";
4
+
5
+ export async function withTimeout<T>(
6
+ asyncFn: (timeoutAndParentSignal?: AbortSignal) => Promise<T>,
7
+ timeoutMs: number,
8
+ signal?: AbortSignal
9
+ ): Promise<T> {
10
+ if (signal?.aborted) {
11
+ throw new ErrorAborted();
12
+ }
13
+
14
+ const timeoutAbortController = new AbortController();
15
+ const timeoutAndParentSignal = anySignal([timeoutAbortController.signal, ...(signal ? [signal] : [])]);
16
+
17
+ async function timeoutPromise(signal: AbortSignal): Promise<never> {
18
+ await sleep(timeoutMs, signal);
19
+ throw new TimeoutError();
20
+ }
21
+
22
+ try {
23
+ return await Promise.race([asyncFn(timeoutAndParentSignal), timeoutPromise(timeoutAndParentSignal)]);
24
+ } finally {
25
+ timeoutAbortController.abort();
26
+ }
27
+ }
package/src/types.ts ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Recursively make all properties optional
3
+ */
4
+ type Primitive = string | number | boolean | bigint | symbol | null | undefined;
5
+ type Builtin = Primitive | Date | Error | RegExp;
6
+
7
+ export type RecursivePartial<T> =
8
+ // stop on built-ins (incl. Error) and functions
9
+ T extends Builtin
10
+ ? T
11
+ : // arrays and readonly arrays
12
+ T extends ReadonlyArray<infer U>
13
+ ? ReadonlyArray<RecursivePartial<U>>
14
+ : T extends Array<infer U>
15
+ ? Array<RecursivePartial<U>>
16
+ : // (optionally: Map/Set support)
17
+ T extends Map<infer K, infer V>
18
+ ? Map<RecursivePartial<K>, RecursivePartial<V>>
19
+ : T extends Set<infer U>
20
+ ? Set<RecursivePartial<U>>
21
+ : // plain objects
22
+ T extends object
23
+ ? {[P in keyof T]?: RecursivePartial<T[P]>}
24
+ : // fallback (shouldn’t be hit often)
25
+ T;
26
+
27
+ /** Type safe wrapper for Number constructor that takes 'any' */
28
+ export function bnToNum(bn: bigint): number {
29
+ return Number(bn);
30
+ }
31
+
32
+ export type NonEmptyArray<T> = [T, ...T[]];
33
+
34
+ /**
35
+ * ArrayToTuple converts an `Array<T>` to `[T, ...T]`
36
+ *
37
+ * eg: `[1, 2, 3]` from type `number[]` to `[number, number, number]`
38
+ */
39
+ export type ArrayToTuple<Tuple extends NonEmptyArray<unknown>> = {
40
+ [Index in keyof Tuple]: Tuple[Index];
41
+ };
42
+
43
+ /**
44
+ * Convert optional attributes of an object to required
45
+ */
46
+ export type RequiredSelective<T, Keys extends keyof T> = T & {
47
+ [K in Keys]-?: T[K];
48
+ };
package/src/url.ts ADDED
@@ -0,0 +1,29 @@
1
+ export function isValidHttpUrl(urlStr: string): boolean {
2
+ let url: URL;
3
+ try {
4
+ url = new URL(urlStr);
5
+
6
+ // `new URL` encodes the username/password with the userinfo percent-encode set.
7
+ // This means the `%` character is not encoded, but others are (such as `=`).
8
+ // If a username/password contain a `%`, they will not be able to be decoded.
9
+ //
10
+ // Make sure that we can successfully decode the username and password here.
11
+ //
12
+ // Unfortunately this means we don't accept every character supported by RFC-3986.
13
+ decodeURIComponent(url.username);
14
+ decodeURIComponent(url.password);
15
+ } catch (_) {
16
+ return false;
17
+ }
18
+
19
+ return url.protocol === "http:" || url.protocol === "https:";
20
+ }
21
+
22
+ /**
23
+ * Sanitize URL to prevent leaking user credentials in logs or metrics
24
+ *
25
+ * Note: `urlStr` must be a valid URL
26
+ */
27
+ export function toPrintableUrl(urlStr: string): string {
28
+ return new URL(urlStr).origin;
29
+ }
@@ -0,0 +1,27 @@
1
+ import {digest, digest64} from "@chainsafe/as-sha256";
2
+
3
+ export function hash(...inputs: Uint8Array[]): Uint8Array {
4
+ return digest(Buffer.concat(inputs));
5
+ }
6
+
7
+ /**
8
+ * Verify that the given ``leaf`` is on the merkle branch ``proof``
9
+ * starting with the given ``root``.
10
+ */
11
+ export function verifyMerkleBranch(
12
+ leaf: Uint8Array,
13
+ proof: Uint8Array[],
14
+ depth: number,
15
+ index: number,
16
+ root: Uint8Array
17
+ ): boolean {
18
+ let value = leaf;
19
+ for (let i = 0; i < depth; i++) {
20
+ if (Math.floor(index / 2 ** i) % 2) {
21
+ value = digest64(Buffer.concat([proof[i], value]));
22
+ } else {
23
+ value = digest64(Buffer.concat([value, proof[i]]));
24
+ }
25
+ }
26
+ return Buffer.from(value.buffer, value.byteOffset, value.byteLength).equals(root);
27
+ }