import { Accessor, createEffect, createSignal, on } from "solid-js"; export const splitAt = ( subject: string, index: number, ): readonly [string, string] => { if (index < 0) { return [subject, ""]; } if (index > subject.length) { return [subject, ""]; } return [subject.slice(0, index), subject.slice(index + 1)]; }; export const toSlug = (subject: string) => subject.toLowerCase().replaceAll(" ", "-").replaceAll(/[^\w-]/gi, ""); export const toHex = (subject: number) => subject.toString(16).padStart(2, "0"); const encoder = new TextEncoder(); export const hash = ( algorithm: AlgorithmIdentifier, subject: Accessor, ) => { const [hash, setHash] = createSignal(); createEffect( on(subject, async (subject) => { if (subject === null || subject === undefined || subject.length === 0) { setHash(undefined); return; } const buffer = new Uint8Array( await crypto.subtle.digest(algorithm, encoder.encode(subject)), ); setHash(Array.from(buffer).map(toHex).join("")); }), ); return hash; }; export const merge = (...objects: Record[]): Record => { if (objects.length === 0) { return {}; } const target = objects[0]; for (const key of new Set(objects.map(o => Object.keys(o)).flat())) { const values = objects.filter(o => Object.hasOwn(o, key)).map(o => o[key]); target[key] = values.every(v => v && typeof v === 'object' && !Array.isArray(v)) ? merge(...values) : values.at(-1); } return target; }; type CamelCase = S extends `${infer First}${infer Rest}` ? `${Lowercase}${Rest}` : Lowercase; export type CamelCased> = { [ K in keyof T as CamelCase]: T[K]; } & {}; export const mapKeysToCamelCase = >(subject: T): CamelCased => Object.fromEntries(Object.entries(subject).map(([k, v]) => [`${k[0].toLowerCase()}${k.slice(1)}`, v])) as CamelCased;