164 lines
4.6 KiB
TypeScript
164 lines
4.6 KiB
TypeScript
import type {
|
|
StorageOptions,
|
|
StoreError,
|
|
StoreEvents,
|
|
StoreEventEmitter,
|
|
} from "@hoppscotch/kernel"
|
|
import * as E from "fp-ts/Either"
|
|
import { getModule } from "."
|
|
import { getKernelMode } from "@hoppscotch/kernel"
|
|
|
|
const STORE_PATH = "hoppscotch-unified.store"
|
|
|
|
let cachedStorePath: string | undefined
|
|
|
|
// These are only defined functions if in desktop mode.
|
|
// For more context, take a look at how `hoppscotch-kernel/.../store/v1/` works
|
|
// and how the `web` mode store kernel ignores the first file directory input.
|
|
let invoke:
|
|
| (<T>(cmd: string, args?: Record<string, unknown>) => Promise<T>)
|
|
| undefined
|
|
let join: ((...paths: string[]) => Promise<string>) | undefined
|
|
|
|
// Single init promise to avoid multiple imports and race conditions
|
|
let initPromise: Promise<void> | undefined
|
|
|
|
const isInitd = async () => {
|
|
if (getKernelMode() !== "desktop") return
|
|
|
|
if (!initPromise) {
|
|
initPromise = Promise.all([
|
|
import("@tauri-apps/api/core").then((module) => {
|
|
invoke = module.invoke
|
|
}),
|
|
import("@tauri-apps/api/path").then((module) => {
|
|
join = module.join
|
|
}),
|
|
]).then(() => {})
|
|
}
|
|
|
|
await initPromise
|
|
}
|
|
|
|
export const getConfigDir = async (): Promise<string> => {
|
|
await isInitd()
|
|
if (!invoke) throw new Error("getConfigDir is only available in desktop mode")
|
|
return await invoke<string>("get_config_dir")
|
|
}
|
|
|
|
export const getBackupDir = async (): Promise<string> => {
|
|
await isInitd()
|
|
if (!invoke) throw new Error("getBackupDir is only available in desktop mode")
|
|
return await invoke<string>("get_backup_dir")
|
|
}
|
|
|
|
export const getLatestDir = async (): Promise<string> => {
|
|
await isInitd()
|
|
if (!invoke) throw new Error("getLatestDir is only available in desktop mode")
|
|
return await invoke<string>("get_latest_dir")
|
|
}
|
|
|
|
export const getStoreDir = async (): Promise<string> => {
|
|
await isInitd()
|
|
if (!invoke) throw new Error("getStoreDir is only available in desktop mode")
|
|
return await invoke<string>("get_store_dir")
|
|
}
|
|
|
|
export const getInstanceDir = async (): Promise<string> => {
|
|
await isInitd()
|
|
if (!invoke)
|
|
throw new Error("getInstanceDir is only available in desktop mode")
|
|
return await invoke<string>("get_instance_dir")
|
|
}
|
|
|
|
const getStorePath = async (): Promise<string> => {
|
|
if (cachedStorePath) return cachedStorePath
|
|
|
|
if (getKernelMode() === "desktop") {
|
|
await isInitd()
|
|
if (join) {
|
|
try {
|
|
const storeDir = await getStoreDir()
|
|
cachedStorePath = await join(storeDir, STORE_PATH)
|
|
return cachedStorePath
|
|
} catch (error) {
|
|
console.error("Failed to get store directory:", error)
|
|
}
|
|
}
|
|
}
|
|
|
|
cachedStorePath = STORE_PATH
|
|
return cachedStorePath
|
|
}
|
|
|
|
export const Store = (() => {
|
|
const module = () => getModule("store")
|
|
|
|
return {
|
|
capabilities: () => module().capabilities,
|
|
|
|
init: async () => {
|
|
const storePath = await getStorePath()
|
|
return module().init(storePath)
|
|
},
|
|
|
|
set: async (
|
|
namespace: string,
|
|
key: string,
|
|
value: unknown,
|
|
options?: StorageOptions
|
|
): Promise<E.Either<StoreError, void>> => {
|
|
const storePath = await getStorePath()
|
|
return module().set(storePath, namespace, key, value, options)
|
|
},
|
|
|
|
get: async <T>(
|
|
namespace: string,
|
|
key: string
|
|
): Promise<E.Either<StoreError, T | undefined>> => {
|
|
const storePath = await getStorePath()
|
|
return module().get<T>(storePath, namespace, key)
|
|
},
|
|
|
|
remove: async (
|
|
namespace: string,
|
|
key: string
|
|
): Promise<E.Either<StoreError, boolean>> => {
|
|
const storePath = await getStorePath()
|
|
return module().remove(storePath, namespace, key)
|
|
},
|
|
|
|
clear: async (namespace?: string): Promise<E.Either<StoreError, void>> => {
|
|
const storePath = await getStorePath()
|
|
return module().clear(storePath, namespace)
|
|
},
|
|
|
|
has: async (
|
|
namespace: string,
|
|
key: string
|
|
): Promise<E.Either<StoreError, boolean>> => {
|
|
const storePath = await getStorePath()
|
|
return module().has(storePath, namespace, key)
|
|
},
|
|
|
|
listNamespaces: async (): Promise<E.Either<StoreError, string[]>> => {
|
|
const storePath = await getStorePath()
|
|
return module().listNamespaces(storePath)
|
|
},
|
|
|
|
listKeys: async (
|
|
namespace: string
|
|
): Promise<E.Either<StoreError, string[]>> => {
|
|
const storePath = await getStorePath()
|
|
return module().listKeys(storePath, namespace)
|
|
},
|
|
|
|
watch: async (
|
|
namespace: string,
|
|
key: string
|
|
): Promise<StoreEventEmitter<StoreEvents>> => {
|
|
const storePath = await getStorePath()
|
|
return module().watch(storePath, namespace, key)
|
|
},
|
|
} as const
|
|
})()
|