better-chatbot / src /lib /utils.ts
Bot
Initial commit for HF Spaces
05c5ed5
Raw
History Blame Contribute Delete
11 kB
import { type ClassValue, clsx } from "clsx";
import { JSONSchema7 } from "json-schema";
import { twMerge } from "tailwind-merge";
import z from "zod";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export const fetcher = async (url: string, options?: RequestInit) => {
const res = await fetch(url, {
redirect: "follow",
cache: "no-store",
...options,
});
if (!res.ok) {
let errorPayload;
try {
errorPayload = await res.json();
} catch {
errorPayload = { message: `Request failed with status ${res.status}` };
}
const error = new Error(
errorPayload.message || "An error occurred while fetching the data.",
);
Object.assign(error, { info: errorPayload, status: res.status });
throw error;
}
return res.json();
};
export const createIncrement =
(i = 0) =>
() =>
i++;
export const noop = () => {};
export const wait = (delay = 0) =>
new Promise<void>((resolve) => setTimeout(resolve, delay));
export const randomRange = (min: number, max: number) =>
Math.floor(Math.random() * (max - min + 1) + min);
export const isString = (value: any): value is string =>
typeof value === "string";
export const isFunction = <
T extends (...args: any[]) => any = (...args: any[]) => any,
>(
v: unknown,
): v is T => typeof v === "function";
export const isObject = (value: any): value is Record<string, any> =>
Object(value) === value;
export const isNull = (value: any): value is null | undefined => value == null;
export const isPromiseLike = (x: unknown): x is PromiseLike<unknown> =>
isFunction((x as any)?.then);
export const isJson = (value: any): value is Record<string, any> => {
try {
if (typeof value === "string") {
const str = value.trim();
JSON.parse(str);
return true;
} else if (isObject(value)) {
return true;
}
return false;
} catch (_e) {
return false;
}
};
export const createDebounce = () => {
let timeout: ReturnType<typeof setTimeout>;
const debounce = (func: (...args: any[]) => any, waitFor = 200) => {
clearTimeout(timeout!);
timeout = setTimeout(() => func(), waitFor);
return timeout;
};
debounce.clear = () => {
clearTimeout(timeout!);
};
return debounce;
};
export const createThrottle = () => {
let lastCall = 0;
let timeout: ReturnType<typeof setTimeout> | null = null;
const throttle = (func: (...args: any[]) => any, waitFor = 200) => {
const now = Date.now();
const timeSinceLastCall = now - lastCall;
if (timeSinceLastCall >= waitFor) {
lastCall = now;
func();
} else {
// Schedule the next call if not already scheduled
if (!timeout) {
const remainingTime = waitFor - timeSinceLastCall;
timeout = setTimeout(() => {
lastCall = Date.now();
func();
timeout = null;
}, remainingTime);
}
}
};
throttle.clear = () => {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
lastCall = 0;
};
return throttle;
};
export const groupBy = <T>(arr: T[], getter: keyof T | ((item: T) => string)) =>
arr.reduce(
(prev, item) => {
const key: string =
getter instanceof Function ? getter(item) : (item[getter] as string);
if (!prev[key]) prev[key] = [];
prev[key].push(item);
return prev;
},
{} as Record<string, T[]>,
);
export const PromiseChain = () => {
let promise: Promise<any> = Promise.resolve();
return <T>(asyncFunction: () => Promise<T>): Promise<T> => {
const resultPromise = promise.then(() => asyncFunction());
promise = resultPromise.catch(() => {});
return resultPromise;
};
};
export const Deferred = <T = void>() => {
let resolve!: T extends void ? (value?: any) => void : (value: T) => void;
let reject!: (reason?: any) => void;
const promise = new Promise<T>((rs, rj) => {
resolve = rs as T extends void ? (value?: any) => void : (value: T) => void;
reject = rj;
});
return {
promise,
reject,
resolve,
};
};
export class Locker {
private promise = Promise.resolve();
private resolve?: () => void;
get isLocked() {
return !!this.resolve;
}
lock() {
this.promise = new Promise((resolve) => {
this.resolve = resolve;
});
}
unlock() {
if (!this.isLocked) return;
this.resolve?.();
this.resolve = undefined;
}
async wait() {
await this.promise;
}
}
export function safeJSONParse<T = unknown>(
json: string,
):
| {
success: true;
value: T;
error?: unknown;
}
| {
success: false;
error: unknown;
value?: T;
} {
try {
const parsed = JSON.parse(json);
return {
success: true,
value: parsed,
};
} catch (e) {
return {
success: false,
error: e,
};
}
}
export function generateUUID(): string {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
export function toAny<T>(value: T): any {
return value;
}
export function errorToString(error: unknown) {
if (error == null) {
return "unknown error";
}
if (typeof error === "string") {
return error;
}
if (error instanceof Error) {
return error.message;
}
return JSON.stringify(error);
}
export function objectFlow<T extends Record<string, any>>(obj: T) {
return {
map: <R>(
fn: (value: T[keyof T], key: keyof T) => R,
): Record<keyof T, R> => {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, fn(value, key)]),
) as Record<keyof T, R>;
},
filter: (
fn: (value: T[keyof T], key: keyof T) => boolean,
): Record<keyof T, T[keyof T]> => {
return Object.fromEntries(
Object.entries(obj).filter(([key, value]) => fn(value, key)),
) as Record<keyof T, T[keyof T]>;
},
forEach: (fn: (value: T[keyof T], key: keyof T) => void): void => {
Object.entries(obj).forEach(([key, value]) => fn(value, key));
},
some: (fn: (value: T[keyof T], key: keyof T) => any): boolean => {
return Object.entries(obj).some(([key, value]) => fn(value, key));
},
every: (fn: (value: T[keyof T], key: keyof T) => any): boolean => {
return Object.entries(obj).every(([key, value]) => fn(value, key));
},
find(fn: (value: T[keyof T], key: keyof T) => any): T | undefined {
return Object.entries(obj).find(([key, value]) => fn(value, key))?.[1];
},
getByPath<U>(path: string[]): U | undefined {
let result: any = obj;
path.find((p) => {
result = result?.[p];
return !result;
});
return result;
},
setByPath(path: string[], value: any) {
path.reduce((acc, cur, i) => {
const isLast = i == path.length - 1;
if (isLast) {
acc[cur] = value;
return acc;
}
acc[cur] ??= {};
return acc[cur];
}, obj as object);
return obj;
},
};
}
export function capitalizeFirstLetter(str: string): string {
if (!str || str.length === 0) return str;
return str.charAt(0).toUpperCase() + str.slice(1);
}
export function truncateString(str: string, maxLength: number): string {
if (str.length <= maxLength) return str;
return str.slice(0, maxLength) + "...";
}
export async function nextTick() {
return new Promise((resolve) => setTimeout(resolve, 0));
}
export function cleanVariableName(input: string = ""): string {
if (!input || typeof input !== "string") {
return "";
}
return input.replace(/[^\w\u0080-\uFFFF-]/g, "").replace(/^[0-9]+/, "");
}
export function generateUniqueKey(key: string, existingKeys: string[]) {
let newKey = key;
let counter = 1;
while (existingKeys.includes(newKey)) {
const baseKey = key.replace(/\d+$/, "");
const hasOriginalNumber = key !== baseKey;
if (hasOriginalNumber) {
const originalNumber = parseInt(key.match(/\d+$/)?.[0] || "0");
newKey = baseKey + (originalNumber + counter);
} else {
newKey = baseKey + counter;
}
counter++;
}
return newKey;
}
export function exclude<T extends object, K extends keyof T>(
obj: T,
keys: K[],
): Omit<T, K> {
return Object.fromEntries(
Object.entries(obj).filter(([key]) => !keys.includes(key as K)),
) as Omit<T, K>;
}
export function validateSchema(key: string, schema: JSONSchema7) {
const variableName = cleanVariableName(key);
if (variableName.length === 0) {
throw new Error("Invalid Variable Name");
}
if (variableName.length > 255) {
throw new Error("Variable Name is too long");
}
if (!schema.type) {
throw new Error("Invalid Schema");
}
if (schema.type == "array" || schema.type == "object") {
const keys = Array.from(Object.keys(schema.properties ?? {}));
if (keys.length != new Set(keys).size) {
throw new Error("Output data must have unique keys");
}
return keys.every((key) => {
return validateSchema(key, schema.properties![key] as JSONSchema7);
});
}
return true;
}
export const createEmitter = () => {
const listeners = new Set<(value: string) => void>();
return {
on: (listener: (value: string) => void) => {
listeners.add(listener);
return () => {
listeners.delete(listener);
};
},
off: (listener: (value: string) => void) => {
listeners.delete(listener);
},
emit: (value: string) => {
listeners.forEach((listener) => listener(value));
},
};
};
export function deduplicateByKey<T>(arr: T[], key: keyof T): T[] {
const seen = new Set<T[keyof T]>();
return arr.filter((item) => {
const keyValue = item[key];
if (seen.has(keyValue)) {
return false;
} else {
seen.add(keyValue);
return true;
}
});
}
export function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
return new Promise<T>((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error("Timeout"));
}, ms);
promise
.then((res) => {
clearTimeout(timer);
resolve(res);
})
.catch((err) => {
clearTimeout(timer);
reject(err);
});
});
}
export function parseEnvBoolean(value: string | boolean | undefined): boolean {
if (typeof value === "boolean") return value;
if (typeof value === "string") {
const lowerVal = value.toLowerCase();
return lowerVal === "true" || lowerVal === "1" || lowerVal === "y";
}
return false;
}
const booleans = ["true", "false", true, false, 1, 0, "1", "0"];
export const booleanCoerced = z
.any()
.refine((val) => booleans.includes(val), { message: "must be boolean" })
.transform((val) => {
if (val === "true" || val === true || val === "1" || val === 1) return true;
return false;
});