Support arbitrary JSON values in kv store

This commit is contained in:
Manav Rathi
2024-09-25 12:16:25 +05:30
parent dd5dae2833
commit 432ef74101
6 changed files with 33 additions and 14 deletions

View File

@@ -9,12 +9,16 @@ import log from "./log";
* storage is limited to the main thread).
*
* The "kv" database consists of one object store, "kv". Keys are strings.
* Values can be strings or number or booleans.
* Values can be arbitrary JSON objects.
*/
interface KVDBSchema extends DBSchema {
kv: {
key: string;
value: string | number | boolean;
/**
* Typescript doesn't have a native JSON type, so this needs to be
* unknown
*/
value: unknown;
};
}
@@ -101,8 +105,16 @@ export const clearKVDB = async () => {
/**
* Return the string value stored corresponding to {@link key}, or `undefined`
* if there is no such entry.
*
* Typescript doesn't have a native JSON type, so the return value is type as an
* `unknown`. For primitive types, you can avoid casting by using the
* {@link getKVS} (string), {@link getKVN} (number) or {@link getKVB} (boolean)
* methods that do an additional runtime check of the type.
*/
export const getKV = async (key: string) => _getKV<string>(key, "string");
export const getKV = async (key: string) => {
const db = await kvDB();
return db.get("kv", key);
};
export const _getKV = async <T extends string | number | boolean>(
key: string,
@@ -113,11 +125,14 @@ export const _getKV = async <T extends string | number | boolean>(
if (v === undefined) return undefined;
if (typeof v != type)
throw new Error(
`Expected the value corresponding to key ${key} to be a ${type}, but instead got ${v}`,
`Expected the value corresponding to key ${key} to be a ${type}, but instead got ${String(v)}`,
);
return v as T;
};
/** String variant of {@link getKV}. */
export const getKVS = async (key: string) => _getKV<string>(key, "string");
/** Numeric variant of {@link getKV}. */
export const getKVN = async (key: string) => _getKV<number>(key, "number");
@@ -127,8 +142,11 @@ export const getKVB = async (key: string) => _getKV<boolean>(key, "boolean");
/**
* Save the given {@link value} corresponding to {@link key}, overwriting any
* existing value.
*
* @param value Any arbitrary JSON object. Typescript doesn't have a native JSON
* type, so this is typed as a unknown
*/
export const setKV = async (key: string, value: string | number | boolean) => {
export const setKV = async (key: string, value: unknown) => {
const db = await kvDB();
await db.put("kv", value, key);
};

View File

@@ -2,7 +2,7 @@
import { ensure } from "@/utils/ensure";
import { z } from "zod";
import { getKV } from "./kv";
import { getKVS } from "./kv";
// TODO: During login the only field present is email. Which makes this
// optionality indicated by these types incorrect.
@@ -57,4 +57,4 @@ export const ensureLocalUser = (): LocalUser => {
* The underlying data is stored in IndexedDB, and can be accessed from web
* workers.
*/
export const ensureAuthToken = async () => ensure(await getKV("token"));
export const ensureAuthToken = async () => ensure(await getKVS("token"));

View File

@@ -1,4 +1,4 @@
import { getKV } from "@/base/kv";
import { getKVS } from "@/base/kv";
/**
* Return the origin (scheme, host, port triple) that should be used for making
@@ -35,7 +35,7 @@ export const apiURL = async (path: string) => (await apiOrigin()) + path;
* Otherwise return undefined.
*/
export const customAPIOrigin = async () =>
(await getKV("apiOrigin")) ??
(await getKVS("apiOrigin")) ??
process.env.NEXT_PUBLIC_ENTE_ENDPOINT ??
undefined;

View File

@@ -1,6 +1,6 @@
import { useIsMobileWidth } from "@/base/hooks";
import { ensureOk } from "@/base/http";
import { getKV, removeKV, setKV } from "@/base/kv";
import { getKVS, removeKV, setKV } from "@/base/kv";
import log from "@/base/log";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import {
@@ -69,7 +69,8 @@ const Contents: React.FC<ContentsProps> = (props) => {
>();
useEffect(
() => void getKV("apiOrigin").then((o) => setInitialAPIOrigin(o ?? "")),
() =>
void getKVS("apiOrigin").then((o) => setInitialAPIOrigin(o ?? "")),
[],
);

View File

@@ -44,7 +44,7 @@ const defaultDiffLimit = 500;
*/
export interface UserEntityChange {
/**
* A UUID or nanoid of the entity.
* The UUID or nanoid of the entity.
*/
id: string;
/**

View File

@@ -1,4 +1,4 @@
import { getKV, removeKV, setKV } from "@/base/kv";
import { getKVS, removeKV, setKV } from "@/base/kv";
import log from "@/base/log";
export enum LS_KEYS {
@@ -78,7 +78,7 @@ export const migrateKVToken = async (user: unknown) => {
typeof oldLSUser == "object" &&
"token" in oldLSUser &&
typeof oldLSUser.token == "string" &&
!(await getKV("token"));
!(await getKVS("token"));
user &&
typeof user == "object" &&