import { createRandomStringGenerator } from "@better-auth/utils/random"; import { type ClassValue, clsx } from "clsx"; import { Data, Effect, Schema } from "effect"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } const randomStringGenerator = createRandomStringGenerator("a-z", "A-Z", "0-9"); export const randomString = (length = 32) => randomStringGenerator(length); export class ParseJsonError extends Data.TaggedError("ParseJsonError")<{ readonly message: string; }> {} export function parseJson(input: unknown): Effect.Effect; export function parseJson>( input: unknown, schema: S, ): Effect.Effect< Schema.Schema.Type, ParseJsonError | Schema.Schema.Encoded, Schema.Schema.Context >; export function parseJson( input: unknown, schema?: Schema.Schema, ): Effect.Effect { return Effect.gen(function* () { let parsed: unknown; if (typeof input === "string") { parsed = yield* Effect.try({ try: () => JSON.parse(input), catch: () => new ParseJsonError({ message: "Invalid JSON string" }), }); } else { parsed = input; } if (schema) { const result = yield* Schema.decodeUnknown(schema)(parsed); return result; } return parsed; }); } export function safeParseJson( input: unknown, defaultValue: A, ): Effect.Effect; export function safeParseJson>( input: unknown, defaultValue: Schema.Schema.Type, schema: S, ): Effect.Effect, never, Schema.Schema.Context>; export function safeParseJson>( input: unknown, defaultValue: unknown, schema?: S, ): Effect.Effect> { const warn = (error: unknown) => { const details = error instanceof Error ? { message: error.message, stack: error.stack } : { error: String(error) }; return Effect.logWarning("safeParseJson fell back to default value", details).pipe( Effect.annotateLogs({ defaultValue: String(defaultValue).slice(0, 100) }), ); }; const withFallback = (effect: Effect.Effect): Effect.Effect => effect.pipe( Effect.tapError(warn), Effect.orElse(() => Effect.succeed(defaultValue as A)), ); return schema ? withFallback(parseJson(input, schema)) : withFallback(parseJson(input)); } export function roundToDecimals(value: number, decimals: number): number { const factor = 10 ** decimals; return Math.round(value * factor) / factor; }