import { Temporal } from "~/common/temporal.js"; import { xxh32r } from "xxh32/dist/raw.js"; /** * @import {Track} from "~/definitions/types.d.ts" */ /** * @template T * @param {Array} array * @returns Array */ export function arrayShuffle(array) { if (array.length === 0) { return []; } array = [...array]; for (let index = array.length - 1; index > 0; index--) { const randArr = crypto.getRandomValues(new Uint32Array(1)); const randVal = randArr[0] / 2 ** 32; const newIndex = Math.floor(randVal * (index + 1)); [array[index], array[newIndex]] = [array[newIndex], array[index]]; } return array; } /** * @param {string | undefined | null} value */ export function boolAttr(value) { return value === ""; } /** * @param {string} a * @param {string} b */ export function compareTimestamps(a, b) { return Temporal.Instant.compare( Temporal.Instant.from(a), Temporal.Instant.from(b), ); } /** * @param {any} object */ export function hash(object) { return xxh32r(jsonEncode(object)).toString(); } /** * @param {Track[]} tracks * @param {Record} initial * @returns {Record} */ export function groupTracksPerScheme( tracks, initial = {}, ) { /** @type {Record} */ const acc = initial; tracks.forEach((track) => { const scheme = track.uri.substring(0, track.uri.indexOf(":")); acc[scheme] ??= []; acc[scheme].push(track); }); return acc; } /** * @param {string[]} uris * @returns {Record} */ export function groupUrisPerScheme(uris) { /** @type {Record} */ const acc = {}; uris.forEach((uri) => { const scheme = uri.substring(0, uri.indexOf(":")); acc[scheme] ??= []; acc[scheme].push(uri); }); return acc; } /** * @param {unknown} test */ export function isPrimitive(test) { return test !== Object(test); } /** * @template T * @param {any} a * @returns {T} */ export function jsonDecode(a) { return JSON.parse(new TextDecoder().decode(a)); } /** * @template T * @param {T} a * @returns Uint8Array */ export function jsonEncode(a) { return new TextEncoder().encode(JSON.stringify(a)); } /** * @template {Record} T * @param {T} rec */ export function removeUndefinedValuesFromRecord(rec) { const recClone = { ...rec }; Object.entries(recClone).forEach(([key, value]) => { if (value === undefined) { delete recClone[key]; } }); return recClone; } /** * @template {Record} T * @param {T} rec */ export function recursivelyCloneRecords(rec) { const recClone = { ...rec }; Object.entries(recClone).forEach(([key, value]) => { if (typeof value === "object") { /** @ts-ignore */ recClone[key] = recursivelyCloneRecords(value); } }); return recClone; } /** * @param {string} str * @returns {string} */ export function safeDecodeURIComponent(str) { return str.replace( /%u([0-9A-Fa-f]{4})|%([0-9A-Fa-f]{2})/g, (_, unicode, byte) => unicode ? String.fromCharCode(parseInt(unicode, 16)) : String.fromCharCode(parseInt(byte, 16)), ); }