import { z } from 'astro:schema' import { typedObjectEntries } from './objects' // eslint-disable-next-line @typescript-eslint/consistent-type-definitions interface JSONObject { [k: string]: JSONValue } type JSONList = JSONValue[] type JSONPrimitive = boolean | number | string | null type JSONValue = Date | JSONList | JSONObject | JSONPrimitive function makeTypedLocalStorage< Schemas extends Record>, T extends { [K in keyof Schemas]: { schema: Schemas[K] default?: z.output | undefined key?: string } }, >(options: T) { return Object.fromEntries( typedObjectEntries(options).map(([originalKey, option]) => { const key = option.key ?? originalKey return [ key, { get: () => { const stringValue = localStorage.getItem(key) if (!stringValue) return option.default let jsonValue: z.output | undefined = option.default try { jsonValue = JSON.parse(stringValue) } catch (error) { console.error(error) return option.default } const parsedValue = option.schema.safeParse(jsonValue) if (!parsedValue.success) { console.error(parsedValue.error) return option.default } return parsedValue.data }, set: (value: z.input) => { localStorage.setItem(key, JSON.stringify(value)) }, remove: () => { localStorage.removeItem(key) }, default: option.default, }, ] }) ) as { [K in keyof T]: { get: () => z.output | (T[K] extends { default: infer D } ? D : undefined) set: (value: z.input) => void remove: () => void default: z.output | undefined } } } export const typedLocalStorage = makeTypedLocalStorage({ pushNotificationsBannerDismissedAt: { schema: z.coerce.date(), }, })