import { crypto } from "jsr:@std/crypto" import { encodeHex } from "jsr:@std/encoding/hex" export const kv = await Deno.openKv() /** * Wraps a promise in a try/catch block and returns a Result object representing * either a successful value or an error. * * This utility function provides a more structured way to handle asynchronous operations * without using try/catch blocks throughout your codebase. It follows a pattern similar * to Rust's Result type, allowing for more predictable error handling. * * @template T - The type of the value returned by the promise on success * @template E - The type of the error object (defaults to Error) * * @param promise - The promise to wrap and execute * * @returns A Promise resolving to a discriminated union object with: * - On success: `{ success: true, value: T, error: null }` * - On failure: `{ success: false, value: null, error: E }` * * @example * // Success case * const successResult = await tryCatch(Promise.resolve('data')); * if (successResult.success) { * console.log(successResult.value); // 'data' * } * * @example * // Error case * const errorResult = await tryCatch(Promise.reject(new Error('Failed'))); * if (!errorResult.success) { * console.error(errorResult.error.message); // 'Failed' * } * * @example * // Using with custom error type * interface ApiError { code: number; message: string } * const apiCall = tryCatch(fetchUserData()); */ export async function tryCatch( promise: Promise, ): Promise< | { success: true value: T error: null } | { success: false value: null error: E } > { try { const value = await promise return { success: true, value, error: null } } catch (error) { return { success: false, value: null, error: error as E } } } /** * Fetches data from a URL with caching. * Cache is stored in the KV * @param url - The URL to fetch data from. * @returns A promise that resolves to an object with success status, value, and error. */ export async function fetchWithCache(url: string): Promise< | { success: true value: T error: null } | { success: false value: null error: string } > { // Calculate the hash of the request URL const keybuffer = new TextEncoder().encode(url) const hashBuffer = await crypto.subtle.digest("SHA-256", keybuffer) const hash = encodeHex(hashBuffer) // Check if the data is already in cache const cached = await kv.get(["fetch-cache", hash]) if (cached.value) { console.info("Serving cached request data") return { success: true, value: cached.value, error: null, } } // Fetch the data from the URL const data = await tryCatch( fetch(url).then((res) => res.json() as T), ) if (!data.success) { return { success: false, value: null, error: data.error.message, } } // Store the fetched data in cache, with a 1-day expiration await kv.set(["fetch-cache", hash], data.value, { expireIn: 60 * 60 * 24 }) return { success: true, value: data.value, error: null, } }