Support arbitrary JSON values in kv store
This commit is contained in:
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 ?? "")),
|
||||
[],
|
||||
);
|
||||
|
||||
|
||||
@@ -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;
|
||||
/**
|
||||
|
||||
@@ -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" &&
|
||||
|
||||
Reference in New Issue
Block a user