119 lines
3.6 KiB
TypeScript
119 lines
3.6 KiB
TypeScript
import { binToHex } from '@bitauth/libauth';
|
|
|
|
// TODO: Put unsorted stuff in here and then try to sort them somewhere more sensible later.
|
|
|
|
export function getOutpointId(outpoint: {
|
|
outpointTransactionHash: Uint8Array;
|
|
outpointIndex: number;
|
|
}): string {
|
|
const outpointHashHex = binToHex(outpoint.outpointTransactionHash);
|
|
|
|
return `${outpointHashHex}:${outpoint.outpointIndex}`;
|
|
}
|
|
|
|
// TODO: Jason uses form timestampMs_entityId
|
|
export function generateActivityId() {
|
|
return Date.now().toString();
|
|
}
|
|
|
|
/**
|
|
* Checks if a URL has duplicate parameters.
|
|
*
|
|
* @param url - The URL to check
|
|
* @returns True if the URL has duplicate parameters, false otherwise
|
|
*/
|
|
export function hasDuplicateParams(url: string | URL): boolean {
|
|
if (typeof url === 'string') {
|
|
url = new URL(url);
|
|
}
|
|
|
|
const params = url.searchParams;
|
|
const seen = new Set<string>();
|
|
|
|
for (const [key] of params.entries()) {
|
|
if (seen.has(key)) {
|
|
return true;
|
|
}
|
|
seen.add(key);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check if the given object is a Record.
|
|
*
|
|
* @param object The variable to check.
|
|
* @returns True if the object is a record.
|
|
*/
|
|
export function isRecord(
|
|
value: unknown,
|
|
): value is Record<string | number | symbol, unknown> {
|
|
if (typeof value !== 'object' || value === null) return false;
|
|
|
|
// Check if its a plain object (not a class instance, array, etc.)
|
|
const prototype = Object.getPrototypeOf(value);
|
|
return prototype === null || prototype === Object.prototype;
|
|
}
|
|
|
|
/**
|
|
* Find all the ocurrences of a given key in a plain JS object.
|
|
* NOTE: This function is intended to search keys in JSON objects
|
|
* and it will not search the key walking through collection object structures(Set, Map, etc.)
|
|
* NOTE: This function is intended to be used to traverse not too nested JSON objects.
|
|
*
|
|
* @param object The plain JS Object to traverse.
|
|
* @param key The Key to search.
|
|
*
|
|
* @throws If object can't be serializated as JSON.
|
|
* @throws If object is more than 100 levels nested.
|
|
* @returns A list of values for the given key.
|
|
*/
|
|
export function findAllKeyOcurrences(
|
|
object: Record<string | number | symbol, unknown>,
|
|
key: string,
|
|
): Array<unknown> {
|
|
// Check object serialization as JSON to avoid circular reference, BigInt values or other object structures(Set, Map, Date, etc..)
|
|
JSON.stringify(object);
|
|
|
|
// Under 100 the resource allocation is excessively fine for any JS environment or hardware.
|
|
// NOTE: If for some reason this needs to be changed be sure to add proper test cases to measure performance.
|
|
// Currently(11/09/2025) the JS stack size can handle roughly 10000 recursive calls on browsers and NodeJS.
|
|
const allowedNestedLevels = 100;
|
|
|
|
const keyValues: Array<unknown> = [];
|
|
|
|
function traverse(
|
|
currentObj: Record<string | number | symbol, unknown>,
|
|
depth: number = 0,
|
|
) {
|
|
// Stop if depth exceeds the amount of 'allowedNestedLevels'.
|
|
if (depth > allowedNestedLevels)
|
|
throw new Error(
|
|
`Maximum nesting level of ${allowedNestedLevels} reached.`,
|
|
);
|
|
|
|
// Stop when find a non object value.
|
|
if (typeof currentObj !== 'object' || currentObj === null) return;
|
|
|
|
if (Array.isArray(currentObj)) {
|
|
currentObj.forEach((item) => traverse(item, depth + 1));
|
|
return;
|
|
}
|
|
|
|
// Check if current object has the key property we are looking
|
|
if (key in currentObj) {
|
|
keyValues.push(currentObj[key]);
|
|
}
|
|
|
|
// Recursively traverse all properties
|
|
Object.values(currentObj).forEach((value) => {
|
|
if (isRecord(value)) traverse(value, depth + 1);
|
|
});
|
|
}
|
|
|
|
traverse(object);
|
|
|
|
return keyValues;
|
|
}
|