mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-05-24 05:35:04 +00:00
refactor: migrate db tauri commands to drizzle
This commit is contained in:
parent
92c69430ff
commit
8fd3223b66
@ -7,10 +7,11 @@ import { decideKkrpcSerialization } from "@/utils/kkrpc"
|
||||
import { sleep } from "@/utils/time"
|
||||
import { trimSlash } from "@/utils/url"
|
||||
import { constructExtensionSupportDir } from "@kksh/api"
|
||||
import { db, spawnExtensionFileServer } from "@kksh/api/commands"
|
||||
import { spawnExtensionFileServer } from "@kksh/api/commands"
|
||||
import type { HeadlessCommand } from "@kksh/api/headless"
|
||||
import { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models"
|
||||
import { constructJarvisServerAPIWithPermissions, type IApp } from "@kksh/api/ui"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { launchNewExtWindow, loadExtensionManifestFromDisk } from "@kksh/extension"
|
||||
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
|
||||
import { convertFileSrc } from "@tauri-apps/api/core"
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { db } from "@kksh/api/commands"
|
||||
import type { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import * as extAPI from "@kksh/extension"
|
||||
import * as path from "@tauri-apps/api/path"
|
||||
import Fuse from "fuse.js"
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
<svelte:window on:keydown={handleKeyDown} />
|
||||
|
||||
<div class="fixed h-12 w-full" data-tauri-drag-region></div>
|
||||
<Layouts.Center class="min-h-screen py-5">
|
||||
<Error.RawErrorJSONPreset
|
||||
title="Error"
|
||||
|
@ -5,8 +5,6 @@
|
||||
import { systemCommands, systemCommandsFiltered } from "@/cmds/system"
|
||||
import AppsCmds from "@/components/main/AppsCmds.svelte"
|
||||
import { i18n } from "@/i18n"
|
||||
import { getUniqueExtensionByIdentifier } from "@/orm/cmds"
|
||||
import { db } from "@/orm/database"
|
||||
import * as m from "@/paraglide/messages"
|
||||
import {
|
||||
appConfig,
|
||||
|
@ -3,8 +3,9 @@
|
||||
import { goHome } from "@/utils/route"
|
||||
import { listenToNewClipboardItem, listenToWindowFocus } from "@/utils/tauri-events"
|
||||
import Icon from "@iconify/svelte"
|
||||
import { ClipboardContentType, db } from "@kksh/api/commands"
|
||||
import { ClipboardContentType } from "@kksh/api/commands"
|
||||
import { SearchModeEnum, SQLSortOrderEnum, type ExtData } from "@kksh/api/models"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { Button, Command, Resizable } from "@kksh/svelte5"
|
||||
import { Constants } from "@kksh/ui"
|
||||
import { CustomCommandInput, GlobalCommandPaletteFooter } from "@kksh/ui/main"
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { cn } from "@/utils"
|
||||
import { db } from "@kksh/api/commands"
|
||||
import type { ExtData } from "@kksh/api/models"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { Resizable, Separator } from "@kksh/svelte5"
|
||||
import { convertFileSrc } from "@tauri-apps/api/core"
|
||||
import DOMPurify from "dompurify"
|
||||
|
@ -10,7 +10,6 @@
|
||||
import { positionToCssStyleString, positionToTailwindClasses } from "@/utils/style"
|
||||
import { sleep } from "@/utils/time"
|
||||
import { isInMainWindow } from "@/utils/window"
|
||||
import { db } from "@kksh/api/commands"
|
||||
import { CustomPosition, ThemeColor, type Position } from "@kksh/api/models"
|
||||
import {
|
||||
constructJarvisServerAPIWithPermissions,
|
||||
@ -19,6 +18,7 @@
|
||||
type IUiCustom
|
||||
} from "@kksh/api/ui"
|
||||
import { toast, type IUiCustomServer1, type IUiCustomServer2 } from "@kksh/api/ui/custom"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { Button } from "@kksh/svelte5"
|
||||
import { cn } from "@kksh/ui/utils"
|
||||
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { KunkunIframeExtParams } from "@/cmds/ext"
|
||||
import { i18n } from "@/i18n"
|
||||
import { appState } from "@/stores/appState"
|
||||
import { db, unregisterExtensionWindow } from "@kksh/api/commands"
|
||||
import type { Ext as ExtInfoInDB, ExtPackageJsonExtra } from "@kksh/api/models"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { loadExtensionManifestFromDisk } from "@kksh/extension"
|
||||
import { error as svError } from "@sveltejs/kit"
|
||||
import { join } from "@tauri-apps/api/path"
|
||||
|
@ -13,7 +13,6 @@
|
||||
} from "@/utils/tauri-events.js"
|
||||
import { sleep } from "@/utils/time.js"
|
||||
import { isInMainWindow } from "@/utils/window.js"
|
||||
import { db } from "@kksh/api/commands"
|
||||
import {
|
||||
constructJarvisServerAPIWithPermissions,
|
||||
type IApp,
|
||||
@ -29,6 +28,7 @@
|
||||
type IComponent,
|
||||
type TemplateUiCommand
|
||||
} from "@kksh/api/ui/template"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { Button, Form } from "@kksh/svelte5"
|
||||
import { LoadingBar } from "@kksh/ui"
|
||||
import { Templates } from "@kksh/ui/extension"
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { KunkunTemplateExtParams } from "@/cmds/ext"
|
||||
import { i18n } from "@/i18n"
|
||||
import { db, unregisterExtensionWindow } from "@kksh/api/commands"
|
||||
import type { Ext as ExtInfoInDB, ExtPackageJsonExtra } from "@kksh/api/models"
|
||||
import type { ExtPackageJsonExtra } from "@kksh/api/models"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { loadExtensionManifestFromDisk } from "@kksh/extension"
|
||||
import { error as sbError, error as svError } from "@sveltejs/kit"
|
||||
import { join } from "@tauri-apps/api/path"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import * as m from "@/paraglide/messages"
|
||||
import { db } from "@kksh/api/commands"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { loadExtensionManifestFromDisk } from "@kksh/extension"
|
||||
import { Button, Dialog, Table } from "@kksh/svelte5"
|
||||
import { join } from "@tauri-apps/api/path"
|
||||
|
@ -7,13 +7,12 @@
|
||||
getUniqueExtensionByPath,
|
||||
searchExtensionData,
|
||||
updateCmdByID
|
||||
} from "@/orm/cmds"
|
||||
} from "@kksh/drizzle/api"
|
||||
import * as schema from "@kksh/drizzle/schema"
|
||||
import { Button, Input } from "@kksh/svelte5"
|
||||
import { CmdTypeEnum, Ext } from "@kunkunapi/src/models/extension"
|
||||
import { SearchModeEnum, SQLSortOrderEnum } from "@kunkunapi/src/models/sql"
|
||||
import { db } from "$lib/orm/database"
|
||||
import * as orm from "drizzle-orm"
|
||||
// import * as orm from "drizzle-orm"
|
||||
import { Inspect } from "svelte-inspect-value"
|
||||
import { toast } from "svelte-sonner"
|
||||
import * as v from "valibot"
|
||||
|
@ -20,9 +20,10 @@ import type {
|
||||
IPath as ITauriPath
|
||||
} from "tauri-api-adapter"
|
||||
import * as v from "valibot"
|
||||
import { KV, type JarvisExtDB } from "../commands/db"
|
||||
import type { fileSearch } from "../commands/fileSearch"
|
||||
import { type AppInfo } from "../models/apps"
|
||||
import { type ExtData } from "../models/extension"
|
||||
import { ExtDataField, SearchMode, SQLSortOrder } from "../models/sql"
|
||||
import type { LightMode, Position, Radius, ThemeColor } from "../models/styles"
|
||||
import type { DenoSysOptions } from "../permissions/schema"
|
||||
|
||||
@ -154,23 +155,34 @@ export interface IUiCustom {
|
||||
}
|
||||
|
||||
export interface IDb {
|
||||
add: typeof JarvisExtDB.prototype.add
|
||||
delete: typeof JarvisExtDB.prototype.delete
|
||||
search: typeof JarvisExtDB.prototype.search
|
||||
retrieveAll: typeof JarvisExtDB.prototype.retrieveAll
|
||||
retrieveAllByType: typeof JarvisExtDB.prototype.retrieveAllByType
|
||||
deleteAll: typeof JarvisExtDB.prototype.deleteAll
|
||||
update: typeof JarvisExtDB.prototype.update
|
||||
add: (data: { data: string; dataType?: string; searchText?: string }) => Promise<void>
|
||||
delete: (dataId: number) => Promise<void>
|
||||
search: (searchParams: {
|
||||
dataId?: number
|
||||
searchMode?: SearchMode
|
||||
dataType?: string
|
||||
searchText?: string
|
||||
afterCreatedAt?: Date
|
||||
beforeCreatedAt?: Date
|
||||
limit?: number
|
||||
orderByCreatedAt?: SQLSortOrder
|
||||
orderByUpdatedAt?: SQLSortOrder
|
||||
fields?: ExtDataField[]
|
||||
}) => Promise<ExtData[]>
|
||||
retrieveAll: (options: { fields?: ExtDataField[] }) => Promise<ExtData[]>
|
||||
retrieveAllByType: (dataType: string) => Promise<ExtData[]>
|
||||
deleteAll: () => Promise<void>
|
||||
update: (data: { dataId: number; data: string; searchText?: string }) => Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
* A key-value store built on top of the Database API (based on sqlite)
|
||||
*/
|
||||
export interface IKV {
|
||||
get: typeof KV.prototype.get
|
||||
set: typeof KV.prototype.set
|
||||
exists: typeof KV.prototype.exists
|
||||
delete: typeof KV.prototype.delete
|
||||
get: <T = string>(key: string) => Promise<T | null | undefined>
|
||||
set: (key: string, value: string) => Promise<void>
|
||||
exists: (key: string) => Promise<boolean>
|
||||
delete: (key: string) => Promise<void>
|
||||
}
|
||||
|
||||
export interface IFs {
|
||||
|
@ -1,465 +0,0 @@
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { array, literal, optional, parse, safeParse, union, type InferOutput } from "valibot"
|
||||
import { KUNKUN_EXT_IDENTIFIER } from "../constants"
|
||||
import { CmdType, Ext, ExtCmd, ExtData } from "../models/extension"
|
||||
import { convertDateToSqliteString, SearchMode, SearchModeEnum, SQLSortOrder } from "../models/sql"
|
||||
import { generateJarvisPluginCommand } from "./common"
|
||||
|
||||
export interface QueryResult {
|
||||
/** The number of rows affected by the query. */
|
||||
rowsAffected: number
|
||||
/**
|
||||
* The last inserted `id`.
|
||||
*
|
||||
* This value is not set for Postgres databases. If the
|
||||
* last inserted id is required on Postgres, the `select` function
|
||||
* must be used, with a `RETURNING` clause
|
||||
* (`INSERT INTO todos (title) VALUES ($1) RETURNING id`).
|
||||
*/
|
||||
lastInsertId?: number
|
||||
}
|
||||
|
||||
export function select(query: string, values: any[]) {
|
||||
return invoke<any[]>(generateJarvisPluginCommand("select"), {
|
||||
query,
|
||||
values
|
||||
})
|
||||
}
|
||||
|
||||
export function execute(query: string, values: any[]) {
|
||||
return invoke<QueryResult>(generateJarvisPluginCommand("execute"), {
|
||||
query,
|
||||
values
|
||||
})
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Extension CRUD */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
export function createExtension(ext: {
|
||||
identifier: string
|
||||
version: string
|
||||
enabled?: boolean
|
||||
path?: string
|
||||
data?: any
|
||||
}) {
|
||||
return invoke<void>(generateJarvisPluginCommand("create_extension"), ext)
|
||||
}
|
||||
|
||||
export function getAllExtensions() {
|
||||
return invoke<Ext[]>(generateJarvisPluginCommand("get_all_extensions"))
|
||||
}
|
||||
|
||||
export function getUniqueExtensionByIdentifier(identifier: string) {
|
||||
return invoke<Ext | undefined>(
|
||||
generateJarvisPluginCommand("get_unique_extension_by_identifier"),
|
||||
{
|
||||
identifier
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function getUniqueExtensionByPath(path: string) {
|
||||
return invoke<Ext | undefined>(generateJarvisPluginCommand("get_unique_extension_by_path"), {
|
||||
path
|
||||
})
|
||||
}
|
||||
|
||||
export function getAllExtensionsByIdentifier(identifier: string) {
|
||||
return invoke<Ext[]>(generateJarvisPluginCommand("get_all_extensions_by_identifier"), {
|
||||
identifier
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this function when you expect the extension to exist. Such as builtin extensions.
|
||||
* @param identifier
|
||||
* @returns
|
||||
*/
|
||||
export function getExtensionByIdentifierExpectExists(identifier: string): Promise<Ext> {
|
||||
return getUniqueExtensionByIdentifier(identifier).then((ext) => {
|
||||
if (!ext) {
|
||||
throw new Error(`Unexpexted Error: Extension ${identifier} not found`)
|
||||
}
|
||||
return ext
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: clean this up
|
||||
// export function deleteExtensionByIdentifier(identifier: string) {
|
||||
// return invoke<void>(generateJarvisPluginCommand("delete_extension_by_identifier"), { identifier })
|
||||
// }
|
||||
|
||||
export function deleteExtensionByPath(path: string) {
|
||||
return invoke<void>(generateJarvisPluginCommand("delete_extension_by_path"), {
|
||||
path
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteExtensionByExtId(extId: string) {
|
||||
return invoke<void>(generateJarvisPluginCommand("delete_extension_by_ext_id"), { extId })
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Extension Command CRUD */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
export function createCommand(data: {
|
||||
extId: number
|
||||
name: string
|
||||
cmdType: CmdType
|
||||
data: string
|
||||
alias?: string
|
||||
hotkey?: string
|
||||
enabled?: boolean
|
||||
}) {
|
||||
return invoke<void>(generateJarvisPluginCommand("create_command"), {
|
||||
...data,
|
||||
enabled: data.enabled ?? false
|
||||
})
|
||||
}
|
||||
|
||||
export function getCommandById(cmdId: number) {
|
||||
return invoke<ExtCmd | undefined>(generateJarvisPluginCommand("get_command_by_id"), { cmdId })
|
||||
}
|
||||
|
||||
export function getCommandsByExtId(extId: number) {
|
||||
return invoke<ExtCmd[]>(generateJarvisPluginCommand("get_commands_by_ext_id"), { extId })
|
||||
}
|
||||
|
||||
export function deleteCommandById(cmdId: number) {
|
||||
return invoke<void>(generateJarvisPluginCommand("delete_command_by_id"), {
|
||||
cmdId
|
||||
})
|
||||
}
|
||||
|
||||
export function updateCommandById(data: {
|
||||
cmdId: number
|
||||
name: string
|
||||
cmdType: CmdType
|
||||
data: string
|
||||
alias?: string
|
||||
hotkey?: string
|
||||
enabled: boolean
|
||||
}) {
|
||||
return invoke<void>(generateJarvisPluginCommand("update_command_by_id"), data)
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Extension Data CRUD */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
export const ExtDataField = union([literal("data"), literal("search_text")])
|
||||
export type ExtDataField = InferOutput<typeof ExtDataField>
|
||||
|
||||
function convertRawExtDataToExtData(rawData?: {
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
data: null | string
|
||||
searchText: null | string
|
||||
}): ExtData | undefined {
|
||||
if (!rawData) {
|
||||
return rawData
|
||||
}
|
||||
const parsedRes = safeParse(ExtData, {
|
||||
...rawData,
|
||||
createdAt: new Date(rawData.createdAt),
|
||||
updatedAt: new Date(rawData.updatedAt),
|
||||
data: rawData.data ?? undefined,
|
||||
searchText: rawData.searchText ?? undefined
|
||||
})
|
||||
if (parsedRes.success) {
|
||||
return parsedRes.output
|
||||
} else {
|
||||
console.error("Extension Data Parse Failure", parsedRes.issues)
|
||||
throw new Error("Fail to parse extension data")
|
||||
}
|
||||
}
|
||||
|
||||
export function createExtensionData(data: {
|
||||
extId: number
|
||||
dataType: string
|
||||
data: string
|
||||
searchText?: string
|
||||
}) {
|
||||
return invoke<void>(generateJarvisPluginCommand("create_extension_data"), data)
|
||||
}
|
||||
|
||||
export function getExtensionDataById(dataId: number, fields?: ExtDataField[]) {
|
||||
return invoke<
|
||||
| (ExtData & {
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
data: null | string
|
||||
searchText: null | string
|
||||
})
|
||||
| undefined
|
||||
>(generateJarvisPluginCommand("get_extension_data_by_id"), {
|
||||
dataId,
|
||||
fields
|
||||
}).then(convertRawExtDataToExtData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields option can be used to select optional fields. By default, if left empty, data and searchText are not returned.
|
||||
* This is because data and searchText can be large and we don't want to return them by default.
|
||||
* If you just want to get data ids in order to delete them, retrieving all data is not necessary.
|
||||
* @param searchParams
|
||||
*/
|
||||
export async function searchExtensionData(searchParams: {
|
||||
extId: number
|
||||
searchMode: SearchMode
|
||||
dataId?: number
|
||||
dataType?: string
|
||||
searchText?: string
|
||||
afterCreatedAt?: string
|
||||
beforeCreatedAt?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
orderByCreatedAt?: SQLSortOrder
|
||||
orderByUpdatedAt?: SQLSortOrder
|
||||
fields?: ExtDataField[]
|
||||
}): Promise<ExtData[]> {
|
||||
const fields = parse(optional(array(ExtDataField), []), searchParams.fields)
|
||||
let items = await invoke<
|
||||
(ExtData & {
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
data: null | string
|
||||
searchText: null | string
|
||||
})[]
|
||||
>(generateJarvisPluginCommand("search_extension_data"), {
|
||||
searchQuery: {
|
||||
...searchParams,
|
||||
fields
|
||||
}
|
||||
})
|
||||
|
||||
return items.map(convertRawExtDataToExtData).filter((item) => item) as ExtData[]
|
||||
}
|
||||
|
||||
export function deleteExtensionDataById(dataId: number) {
|
||||
return invoke<void>(generateJarvisPluginCommand("delete_extension_data_by_id"), { dataId })
|
||||
}
|
||||
|
||||
export function updateExtensionDataById(data: {
|
||||
dataId: number
|
||||
data: string
|
||||
searchText?: string
|
||||
}) {
|
||||
return invoke<void>(generateJarvisPluginCommand("update_extension_data_by_id"), data)
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Built-in Extensions */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
export function getExtClipboard() {
|
||||
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_CLIPBOARD_EXT_IDENTIFIER)
|
||||
}
|
||||
export function getExtQuickLinks() {
|
||||
return getExtensionByIdentifierExpectExists(
|
||||
KUNKUN_EXT_IDENTIFIER.KUNKUN_QUICK_LINKS_EXT_IDENTIFIER
|
||||
)
|
||||
}
|
||||
export function getExtRemote() {
|
||||
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_REMOTE_EXT_IDENTIFIER)
|
||||
}
|
||||
export function getExtScriptCmd() {
|
||||
return getExtensionByIdentifierExpectExists(
|
||||
KUNKUN_EXT_IDENTIFIER.KUNKUN_SCRIPT_CMD_EXT_IDENTIFIER
|
||||
)
|
||||
}
|
||||
export function getExtDev() {
|
||||
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_DEV_EXT_IDENTIFIER)
|
||||
}
|
||||
|
||||
/**
|
||||
* Database API for extensions.
|
||||
* Extensions shouldn't have full access to the database, they can only access their own data.
|
||||
* When an extension is loaded, the main thread will create an instance of this class and
|
||||
* expose it to the extension.
|
||||
*/
|
||||
export class JarvisExtDB {
|
||||
extId: number
|
||||
|
||||
constructor(extId: number) {
|
||||
this.extId = extId
|
||||
}
|
||||
|
||||
async add(data: { data: string; dataType?: string; searchText?: string }) {
|
||||
return createExtensionData({
|
||||
data: data.data,
|
||||
dataType: data.dataType ?? "default",
|
||||
searchText: data.searchText,
|
||||
extId: this.extId
|
||||
})
|
||||
}
|
||||
|
||||
async delete(dataId: number): Promise<void> {
|
||||
// Verify if this data belongs to this extension
|
||||
const d = await getExtensionDataById(dataId)
|
||||
if (!d || d.extId !== this.extId) {
|
||||
throw new Error("Extension Data not found")
|
||||
}
|
||||
return await deleteExtensionDataById(dataId)
|
||||
}
|
||||
|
||||
async search(searchParams: {
|
||||
dataId?: number
|
||||
searchMode?: SearchMode
|
||||
dataType?: string
|
||||
searchText?: string
|
||||
afterCreatedAt?: Date
|
||||
beforeCreatedAt?: Date
|
||||
limit?: number
|
||||
orderByCreatedAt?: SQLSortOrder
|
||||
orderByUpdatedAt?: SQLSortOrder
|
||||
fields?: ExtDataField[]
|
||||
}): Promise<ExtData[]> {
|
||||
const beforeCreatedAt = searchParams.beforeCreatedAt
|
||||
? convertDateToSqliteString(searchParams.beforeCreatedAt)
|
||||
: undefined
|
||||
const afterCreatedAt = searchParams.afterCreatedAt
|
||||
? convertDateToSqliteString(searchParams.afterCreatedAt)
|
||||
: undefined
|
||||
return searchExtensionData({
|
||||
...searchParams,
|
||||
searchMode: searchParams.searchMode ?? SearchModeEnum.FTS,
|
||||
extId: this.extId,
|
||||
beforeCreatedAt,
|
||||
afterCreatedAt
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all data of this extension.
|
||||
* Use `search()` method for more advanced search.
|
||||
* @param options optional fields to retrieve. By default, data and searchText are not returned.
|
||||
* @returns
|
||||
*/
|
||||
retrieveAll(options: { fields?: ExtDataField[] }): Promise<ExtData[]> {
|
||||
return this.search({ fields: options.fields })
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all data of this extension by type.
|
||||
* Use `search()` method for more advanced search.
|
||||
* @param dataType
|
||||
* @returns
|
||||
*/
|
||||
retrieveAllByType(dataType: string): Promise<ExtData[]> {
|
||||
return this.search({ dataType })
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all data of this extension.
|
||||
*/
|
||||
deleteAll(): Promise<void> {
|
||||
return this.search({})
|
||||
.then((items) => {
|
||||
return Promise.all(items.map((item) => this.delete(item.dataId)))
|
||||
})
|
||||
.then(() => {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data and searchText of this extension.
|
||||
* @param dataId unique id of the data
|
||||
* @param data
|
||||
* @param searchText
|
||||
* @returns
|
||||
*/
|
||||
async update(data: { dataId: number; data: string; searchText?: string }): Promise<void> {
|
||||
const d = await getExtensionDataById(data.dataId)
|
||||
if (!d || d.extId !== this.extId) {
|
||||
throw new Error("Extension Data not found")
|
||||
}
|
||||
return updateExtensionDataById(data)
|
||||
}
|
||||
}
|
||||
|
||||
export class KV {
|
||||
extId: number
|
||||
db: JarvisExtDB
|
||||
private DataType: string = "kunkun_kv"
|
||||
|
||||
constructor(extId: number) {
|
||||
this.extId = extId
|
||||
this.db = new JarvisExtDB(extId)
|
||||
}
|
||||
|
||||
get<T = string>(key: string): Promise<T | null | undefined> {
|
||||
return this.db
|
||||
.search({
|
||||
dataType: this.DataType,
|
||||
searchText: key,
|
||||
searchMode: SearchModeEnum.ExactMatch,
|
||||
fields: ["search_text", "data"]
|
||||
})
|
||||
.then((items) => {
|
||||
if (items.length === 0) {
|
||||
return null
|
||||
} else if (items.length > 1) {
|
||||
throw new Error("Multiple KVs with the same key")
|
||||
}
|
||||
return items[0].data ? (JSON.parse(items[0].data).value as T) : null
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn(err)
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
set(key: string, value: string): Promise<void> {
|
||||
return this.db
|
||||
.search({
|
||||
dataType: this.DataType,
|
||||
searchText: key,
|
||||
searchMode: SearchModeEnum.ExactMatch
|
||||
})
|
||||
.then((items) => {
|
||||
if (items.length === 0) {
|
||||
return this.db.add({
|
||||
data: JSON.stringify({ value: value }),
|
||||
dataType: this.DataType,
|
||||
searchText: key
|
||||
})
|
||||
} else if (items.length === 1) {
|
||||
return this.db.update({
|
||||
dataId: items[0].dataId,
|
||||
data: JSON.stringify({ value: value }),
|
||||
searchText: key
|
||||
})
|
||||
} else {
|
||||
return Promise.all(items.map((item) => this.db.delete(item.dataId))).then(() =>
|
||||
Promise.resolve()
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
delete(key: string): Promise<void> {
|
||||
return this.db
|
||||
.search({
|
||||
dataType: this.DataType,
|
||||
searchText: key,
|
||||
searchMode: SearchModeEnum.ExactMatch
|
||||
})
|
||||
.then((items) => {
|
||||
return Promise.all(items.map((item) => this.db.delete(item.dataId))).then(() =>
|
||||
Promise.resolve()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
exists(key: string): Promise<boolean> {
|
||||
return this.db
|
||||
.search({
|
||||
dataType: this.DataType,
|
||||
searchText: key,
|
||||
searchMode: SearchModeEnum.ExactMatch,
|
||||
fields: []
|
||||
})
|
||||
.then((items) => {
|
||||
return items.length > 0
|
||||
})
|
||||
}
|
||||
}
|
@ -5,8 +5,7 @@ export * from "./tools"
|
||||
export * from "./extension"
|
||||
export * from "./system"
|
||||
export * from "./store"
|
||||
export * as db from "./db"
|
||||
export { JarvisExtDB } from "./db"
|
||||
export * as sql from "./sql"
|
||||
export * from "./clipboard"
|
||||
export * from "./fileSearch"
|
||||
export * from "./utils"
|
||||
|
30
packages/api/src/commands/sql.ts
Normal file
30
packages/api/src/commands/sql.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { generateJarvisPluginCommand } from "./common"
|
||||
|
||||
export interface QueryResult {
|
||||
/** The number of rows affected by the query. */
|
||||
rowsAffected: number
|
||||
/**
|
||||
* The last inserted `id`.
|
||||
*
|
||||
* This value is not set for Postgres databases. If the
|
||||
* last inserted id is required on Postgres, the `select` function
|
||||
* must be used, with a `RETURNING` clause
|
||||
* (`INSERT INTO todos (title) VALUES ($1) RETURNING id`).
|
||||
*/
|
||||
lastInsertId?: number
|
||||
}
|
||||
|
||||
export function select(query: string, values: any[]) {
|
||||
return invoke<any[]>(generateJarvisPluginCommand("select"), {
|
||||
query,
|
||||
values
|
||||
})
|
||||
}
|
||||
|
||||
export function execute(query: string, values: any[]) {
|
||||
return invoke<QueryResult>(generateJarvisPluginCommand("execute"), {
|
||||
query,
|
||||
values
|
||||
})
|
||||
}
|
@ -18,10 +18,7 @@ export const Ext = v.object({
|
||||
extId: v.number(),
|
||||
identifier: v.string(),
|
||||
version: v.string(),
|
||||
enabled: v.pipe(
|
||||
v.number(),
|
||||
v.transform((input) => Boolean(input))
|
||||
),
|
||||
enabled: v.boolean(),
|
||||
installedAt: v.string(),
|
||||
path: v.optional(v.nullable(v.string())),
|
||||
data: v.optional(v.any())
|
||||
@ -49,11 +46,9 @@ export const ExtCmd = v.object({
|
||||
data: v.string(),
|
||||
alias: v.optional(v.nullable(v.string())),
|
||||
hotkey: v.optional(v.nullable(v.string())),
|
||||
enabled: v.pipe(
|
||||
v.number(),
|
||||
v.transform((input) => Boolean(input))
|
||||
)
|
||||
enabled: v.boolean()
|
||||
})
|
||||
|
||||
export type ExtCmd = v.InferOutput<typeof ExtCmd>
|
||||
|
||||
export const QuickLinkCmd = v.object({
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { enum_, type InferOutput } from "valibot"
|
||||
import * as v from "valibot"
|
||||
|
||||
export enum SQLSortOrderEnum {
|
||||
Asc = "ASC",
|
||||
Desc = "DESC"
|
||||
}
|
||||
|
||||
export const SQLSortOrder = enum_(SQLSortOrderEnum)
|
||||
export type SQLSortOrder = InferOutput<typeof SQLSortOrder>
|
||||
export const SQLSortOrder = v.enum_(SQLSortOrderEnum)
|
||||
export type SQLSortOrder = v.InferOutput<typeof SQLSortOrder>
|
||||
|
||||
export enum SearchModeEnum {
|
||||
ExactMatch = "exact_match",
|
||||
@ -14,8 +14,8 @@ export enum SearchModeEnum {
|
||||
FTS = "fts"
|
||||
}
|
||||
|
||||
export const SearchMode = enum_(SearchModeEnum)
|
||||
export type SearchMode = InferOutput<typeof SearchMode>
|
||||
export const SearchMode = v.enum_(SearchModeEnum)
|
||||
export type SearchMode = v.InferOutput<typeof SearchMode>
|
||||
|
||||
export function convertDateToSqliteString(date: Date) {
|
||||
const pad = (num: number) => num.toString().padStart(2, "0")
|
||||
@ -29,3 +29,6 @@ export function convertDateToSqliteString(date: Date) {
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
export const ExtDataField = v.union([v.literal("data"), v.literal("search_text")])
|
||||
export type ExtDataField = v.InferOutput<typeof ExtDataField>
|
||||
|
@ -17,7 +17,8 @@ export const extensions = sqliteTable("extensions", {
|
||||
extId: integer("ext_id").primaryKey({ autoIncrement: true }),
|
||||
identifier: text().notNull(),
|
||||
version: text().notNull(),
|
||||
enabled: numeric().default(sql`(TRUE)`),
|
||||
// enabled: numeric().default(sql`(TRUE)`),
|
||||
enabled: integer({ mode: "boolean" }),
|
||||
path: text(),
|
||||
data: numeric(),
|
||||
installedAt: numeric("installed_at").default(sql`(CURRENT_TIMESTAMP)`)
|
||||
|
@ -1,2 +1,3 @@
|
||||
export * as schema from "./drizzle/schema"
|
||||
export * as relations from "./drizzle/relations"
|
||||
export * as db from "./src/apis"
|
||||
|
@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"exports": {
|
||||
".": "./index.ts",
|
||||
"./api": "./src/apis.ts",
|
||||
"./schema": "./drizzle/schema.ts",
|
||||
"./relations": "./drizzle/relations.ts"
|
||||
},
|
||||
|
@ -1,53 +1,80 @@
|
||||
import * as relations from "@kksh/drizzle/relations"
|
||||
import * as schema from "../drizzle/schema"
|
||||
import { KUNKUN_EXT_IDENTIFIER, type IDb, type IKV } from "@kksh/api"
|
||||
import {
|
||||
CmdType,
|
||||
Ext,
|
||||
ExtCmd,
|
||||
ExtData,
|
||||
SearchMode,
|
||||
SearchModeEnum,
|
||||
SQLSortOrder,
|
||||
SQLSortOrderEnum
|
||||
CmdType,
|
||||
convertDateToSqliteString,
|
||||
Ext,
|
||||
ExtCmd,
|
||||
ExtData,
|
||||
SearchMode,
|
||||
SearchModeEnum,
|
||||
SQLSortOrder,
|
||||
SQLSortOrderEnum
|
||||
} from "@kksh/api/models"
|
||||
import * as relations from "@kksh/drizzle/relations"
|
||||
import * as orm from "drizzle-orm"
|
||||
import type { SelectedFields } from "drizzle-orm/sqlite-core"
|
||||
import * as v from "valibot"
|
||||
import * as schema from "../drizzle/schema"
|
||||
import { db } from "./proxy"
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Built-in Extensions */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
export function getExtClipboard() {
|
||||
// return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_CLIPBOARD_EXT_IDENTIFIER)
|
||||
export function getExtClipboard(): Promise<Ext> {
|
||||
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_CLIPBOARD_EXT_IDENTIFIER)
|
||||
}
|
||||
export function getExtQuickLinks() {
|
||||
// return getExtensionByIdentifierExpectExists(
|
||||
// KUNKUN_EXT_IDENTIFIER.KUNKUN_QUICK_LINKS_EXT_IDENTIFIER
|
||||
// )
|
||||
export function getExtQuickLinks(): Promise<Ext> {
|
||||
return getExtensionByIdentifierExpectExists(
|
||||
KUNKUN_EXT_IDENTIFIER.KUNKUN_QUICK_LINKS_EXT_IDENTIFIER
|
||||
)
|
||||
}
|
||||
export function getExtRemote() {
|
||||
// return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_REMOTE_EXT_IDENTIFIER)
|
||||
export function getExtRemote(): Promise<Ext> {
|
||||
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_REMOTE_EXT_IDENTIFIER)
|
||||
}
|
||||
export function getExtScriptCmd() {
|
||||
// return getExtensionByIdentifierExpectExists(
|
||||
// KUNKUN_EXT_IDENTIFIER.KUNKUN_SCRIPT_CMD_EXT_IDENTIFIER
|
||||
// )
|
||||
export function getExtScriptCmd(): Promise<Ext> {
|
||||
return getExtensionByIdentifierExpectExists(
|
||||
KUNKUN_EXT_IDENTIFIER.KUNKUN_SCRIPT_CMD_EXT_IDENTIFIER
|
||||
)
|
||||
}
|
||||
export function getExtDev() {
|
||||
// return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_DEV_EXT_IDENTIFIER)
|
||||
export function getExtDev(): Promise<Ext> {
|
||||
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_DEV_EXT_IDENTIFIER)
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Extension CRUD */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
export function createExtension(ext: {
|
||||
identifier: string
|
||||
version: string
|
||||
enabled?: boolean
|
||||
path?: string
|
||||
data?: any
|
||||
}) {
|
||||
return db
|
||||
.insert(schema.extensions)
|
||||
.values({
|
||||
identifier: ext.identifier,
|
||||
version: ext.version,
|
||||
enabled: ext.enabled,
|
||||
path: ext.path,
|
||||
data: ext.data
|
||||
})
|
||||
.run()
|
||||
}
|
||||
|
||||
export async function getUniqueExtensionByIdentifier(identifier: string): Promise<Ext | undefined> {
|
||||
const ext = await db
|
||||
.select()
|
||||
.from(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.identifier, identifier))
|
||||
.get()
|
||||
return v.parse(v.optional(Ext), ext)
|
||||
const ext = await db
|
||||
.select()
|
||||
.from(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.identifier, identifier))
|
||||
.get()
|
||||
const result = v.safeParse(v.optional(Ext), ext)
|
||||
if (!result.success) {
|
||||
console.error("Failed to parse extension:", v.flatten(result.issues))
|
||||
return undefined
|
||||
}
|
||||
const parsed = result.output
|
||||
return parsed
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,17 +83,17 @@ export async function getUniqueExtensionByIdentifier(identifier: string): Promis
|
||||
* @returns
|
||||
*/
|
||||
export function getExtensionByIdentifierExpectExists(identifier: string): Promise<Ext> {
|
||||
return getUniqueExtensionByIdentifier(identifier).then((ext) => {
|
||||
if (!ext) {
|
||||
throw new Error(`Unexpexted Error: Extension ${identifier} not found`)
|
||||
}
|
||||
return ext
|
||||
})
|
||||
return getUniqueExtensionByIdentifier(identifier).then((ext) => {
|
||||
if (!ext) {
|
||||
throw new Error(`Unexpexted Error: Extension ${identifier} not found`)
|
||||
}
|
||||
return ext
|
||||
})
|
||||
}
|
||||
|
||||
export async function getAllExtensions(): Promise<Ext[]> {
|
||||
const exts = await db.select().from(schema.extensions).all()
|
||||
return v.parse(v.array(Ext), exts)
|
||||
const exts = await db.select().from(schema.extensions).all()
|
||||
return v.parse(v.array(Ext), exts)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,112 +102,121 @@ export async function getAllExtensions(): Promise<Ext[]> {
|
||||
* @param path
|
||||
*/
|
||||
export async function getUniqueExtensionByPath(path: string) {
|
||||
const ext = await db
|
||||
.select()
|
||||
.from(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.path, path))
|
||||
.get()
|
||||
return v.parse(Ext, ext)
|
||||
const ext = await db
|
||||
.select()
|
||||
.from(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.path, path))
|
||||
.get()
|
||||
return v.parse(Ext, ext)
|
||||
}
|
||||
|
||||
export function getAllExtensionsByIdentifier(identifier: string): Promise<Ext[]> {
|
||||
return db
|
||||
.select()
|
||||
.from(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.identifier, identifier))
|
||||
.all()
|
||||
.then((exts) => v.parse(v.array(Ext), exts))
|
||||
return db
|
||||
.select()
|
||||
.from(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.identifier, identifier))
|
||||
.all()
|
||||
.then((exts) => v.parse(v.array(Ext), exts))
|
||||
}
|
||||
|
||||
export function deleteExtensionByPath(path: string): Promise<void> {
|
||||
return db
|
||||
.delete(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.path, path))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
return db
|
||||
.delete(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.path, path))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
}
|
||||
|
||||
export function deleteExtensionByExtId(extId: number): Promise<void> {
|
||||
return db
|
||||
.delete(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.extId, extId))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
return db
|
||||
.delete(schema.extensions)
|
||||
.where(orm.eq(schema.extensions.extId, extId))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Extension Command CRUD */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
// export async function getExtensionWithCmdsByIdentifier(identifier: string): Promise<ExtWithCmds> {
|
||||
// const ext = await db
|
||||
// .select({
|
||||
// ...schema.extensions,
|
||||
// commands: relations.commandsRelations
|
||||
// })
|
||||
// .from(schema.extensions)
|
||||
// .leftJoin(schema.commands, orm.eq(schema.extensions.extId, schema.commands.extId))
|
||||
// .where(orm.eq(schema.extensions.identifier, identifier))
|
||||
// .get()
|
||||
|
||||
// // return v.parse(v.nullable(ExtWithCmds), ext);
|
||||
// }
|
||||
export function createCommand(data: {
|
||||
extId: number
|
||||
name: string
|
||||
cmdType: CmdType
|
||||
data: string
|
||||
alias?: string
|
||||
hotkey?: string
|
||||
enabled?: boolean
|
||||
}) {
|
||||
return db
|
||||
.insert(schema.commands)
|
||||
.values({
|
||||
extId: data.extId,
|
||||
name: data.name,
|
||||
type: data.cmdType,
|
||||
data: data.data,
|
||||
alias: data.alias,
|
||||
hotkey: data.hotkey,
|
||||
enabled: data.enabled ?? true
|
||||
})
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
}
|
||||
|
||||
export async function getCmdById(cmdId: number): Promise<ExtCmd> {
|
||||
const cmd = await db
|
||||
.select()
|
||||
.from(schema.commands)
|
||||
.where(orm.eq(schema.commands.cmdId, cmdId))
|
||||
.get()
|
||||
return v.parse(ExtCmd, cmd)
|
||||
const cmd = await db
|
||||
.select()
|
||||
.from(schema.commands)
|
||||
.where(orm.eq(schema.commands.cmdId, cmdId))
|
||||
.get()
|
||||
return v.parse(ExtCmd, cmd)
|
||||
}
|
||||
|
||||
export async function getAllCmds(): Promise<ExtCmd[]> {
|
||||
const cmds = await db.select().from(schema.commands).all()
|
||||
return v.parse(v.array(ExtCmd), cmds)
|
||||
const cmds = await db.select().from(schema.commands).all()
|
||||
return v.parse(v.array(ExtCmd), cmds)
|
||||
}
|
||||
|
||||
export function getCommandsByExtId(extId: number) {
|
||||
return db
|
||||
.select()
|
||||
.from(schema.commands)
|
||||
.where(orm.eq(schema.commands.extId, extId))
|
||||
.all()
|
||||
.then((cmds) => v.parse(v.array(ExtCmd), cmds))
|
||||
return db
|
||||
.select()
|
||||
.from(schema.commands)
|
||||
.where(orm.eq(schema.commands.extId, extId))
|
||||
.all()
|
||||
.then((cmds) => v.parse(v.array(ExtCmd), cmds))
|
||||
}
|
||||
|
||||
export function deleteCmdById(cmdId: number) {
|
||||
return db
|
||||
.delete(schema.commands)
|
||||
.where(orm.eq(schema.commands.cmdId, cmdId))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
return db
|
||||
.delete(schema.commands)
|
||||
.where(orm.eq(schema.commands.cmdId, cmdId))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
}
|
||||
|
||||
export function updateCmdByID(data: {
|
||||
cmdId: number
|
||||
name: string
|
||||
cmdType: CmdType
|
||||
data: string
|
||||
alias?: string
|
||||
hotkey?: string
|
||||
enabled: boolean
|
||||
cmdId: number
|
||||
name: string
|
||||
cmdType: CmdType
|
||||
data: string
|
||||
alias?: string
|
||||
hotkey?: string
|
||||
enabled: boolean
|
||||
}) {
|
||||
return db
|
||||
.update(schema.commands)
|
||||
.set({
|
||||
name: data.name,
|
||||
type: data.cmdType,
|
||||
data: data.data,
|
||||
alias: data.alias, // optional
|
||||
hotkey: data.hotkey, // optional
|
||||
enabled: data.enabled
|
||||
// in drizzle schema, use integer({ mode: 'boolean' }) for boolean sqlite
|
||||
// enabled: data.enabled ? String(data.enabled) : undefined
|
||||
})
|
||||
.where(orm.eq(schema.commands.cmdId, data.cmdId))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
return db
|
||||
.update(schema.commands)
|
||||
.set({
|
||||
name: data.name,
|
||||
type: data.cmdType,
|
||||
data: data.data,
|
||||
alias: data.alias, // optional
|
||||
hotkey: data.hotkey, // optional
|
||||
enabled: data.enabled
|
||||
// in drizzle schema, use integer({ mode: 'boolean' }) for boolean sqlite
|
||||
// enabled: data.enabled ? String(data.enabled) : undefined
|
||||
})
|
||||
.where(orm.eq(schema.commands.cmdId, data.cmdId))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@ -190,200 +226,391 @@ export const ExtDataField = v.union([v.literal("data"), v.literal("search_text")
|
||||
export type ExtDataField = v.InferOutput<typeof ExtDataField>
|
||||
|
||||
function convertRawExtDataToExtData(rawData?: {
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
data: null | string
|
||||
searchText?: null | string
|
||||
dataId: number
|
||||
extId: number
|
||||
dataType: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
data: null | string
|
||||
searchText?: null | string
|
||||
dataId: number
|
||||
extId: number
|
||||
dataType: string
|
||||
}): ExtData | undefined {
|
||||
if (!rawData) {
|
||||
return rawData
|
||||
}
|
||||
const parsedRes = v.safeParse(ExtData, {
|
||||
...rawData,
|
||||
createdAt: new Date(rawData.createdAt),
|
||||
updatedAt: new Date(rawData.updatedAt),
|
||||
data: rawData.data ?? undefined,
|
||||
searchText: rawData.searchText ?? undefined
|
||||
})
|
||||
if (parsedRes.success) {
|
||||
return parsedRes.output
|
||||
} else {
|
||||
console.error("Extension Data Parse Failure", parsedRes.issues)
|
||||
throw new Error("Fail to parse extension data")
|
||||
}
|
||||
if (!rawData) {
|
||||
return rawData
|
||||
}
|
||||
const parsedRes = v.safeParse(ExtData, {
|
||||
...rawData,
|
||||
createdAt: new Date(rawData.createdAt),
|
||||
updatedAt: new Date(rawData.updatedAt),
|
||||
data: rawData.data ?? undefined,
|
||||
searchText: rawData.searchText ?? undefined
|
||||
})
|
||||
if (parsedRes.success) {
|
||||
return parsedRes.output
|
||||
} else {
|
||||
console.error("Extension Data Parse Failure", parsedRes.issues)
|
||||
throw new Error("Fail to parse extension data")
|
||||
}
|
||||
}
|
||||
|
||||
export function createExtensionData(data: {
|
||||
extId: number
|
||||
dataType: string
|
||||
data: string
|
||||
searchText?: string
|
||||
extId: number
|
||||
dataType: string
|
||||
data: string
|
||||
searchText?: string
|
||||
}) {
|
||||
return db.insert(schema.extensionData).values(data).run()
|
||||
return db.insert(schema.extensionData).values(data).run()
|
||||
}
|
||||
|
||||
export function getExtensionDataById(dataId: number, fields?: ExtDataField[]) {
|
||||
const _fields = fields ?? []
|
||||
const selectQuery: SelectedFields = {
|
||||
dataId: schema.extensionData.dataId,
|
||||
extId: schema.extensionData.extId,
|
||||
dataType: schema.extensionData.dataType,
|
||||
metadata: schema.extensionData.metadata,
|
||||
createdAt: schema.extensionData.createdAt,
|
||||
updatedAt: schema.extensionData.updatedAt
|
||||
// data: schema.extensionData.data,
|
||||
// searchText: schema.extensionData.searchText
|
||||
}
|
||||
if (_fields.includes("data")) {
|
||||
selectQuery["data"] = schema.extensionData.data
|
||||
}
|
||||
if (_fields.includes("search_text")) {
|
||||
selectQuery["searchText"] = schema.extensionData.searchText
|
||||
}
|
||||
return db
|
||||
.select(selectQuery)
|
||||
.from(schema.extensionData)
|
||||
.where(orm.eq(schema.extensionData.dataId, dataId))
|
||||
.get()
|
||||
.then((rawData) => {
|
||||
console.log("Raw Data", rawData)
|
||||
// @ts-expect-error - rawData is unknown, but will be safe parsed with valibot
|
||||
return convertRawExtDataToExtData(rawData)
|
||||
})
|
||||
const _fields = fields ?? []
|
||||
const selectQuery: SelectedFields = {
|
||||
dataId: schema.extensionData.dataId,
|
||||
extId: schema.extensionData.extId,
|
||||
dataType: schema.extensionData.dataType,
|
||||
metadata: schema.extensionData.metadata,
|
||||
createdAt: schema.extensionData.createdAt,
|
||||
updatedAt: schema.extensionData.updatedAt
|
||||
// data: schema.extensionData.data,
|
||||
// searchText: schema.extensionData.searchText
|
||||
}
|
||||
if (_fields.includes("data")) {
|
||||
selectQuery["data"] = schema.extensionData.data
|
||||
}
|
||||
if (_fields.includes("search_text")) {
|
||||
selectQuery["searchText"] = schema.extensionData.searchText
|
||||
}
|
||||
return db
|
||||
.select(selectQuery)
|
||||
.from(schema.extensionData)
|
||||
.where(orm.eq(schema.extensionData.dataId, dataId))
|
||||
.get()
|
||||
.then((rawData) => {
|
||||
console.log("Raw Data", rawData)
|
||||
// @ts-expect-error - rawData is unknown, but will be safe parsed with valibot
|
||||
return convertRawExtDataToExtData(rawData)
|
||||
})
|
||||
}
|
||||
|
||||
export async function searchExtensionData(searchParams: {
|
||||
extId: number
|
||||
searchMode: SearchMode
|
||||
dataId?: number
|
||||
dataType?: string
|
||||
searchText?: string
|
||||
afterCreatedAt?: string
|
||||
beforeCreatedAt?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
orderByCreatedAt?: SQLSortOrder
|
||||
orderByUpdatedAt?: SQLSortOrder
|
||||
fields?: ExtDataField[]
|
||||
extId: number
|
||||
searchMode: SearchMode
|
||||
dataId?: number
|
||||
dataType?: string
|
||||
searchText?: string
|
||||
afterCreatedAt?: string
|
||||
beforeCreatedAt?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
orderByCreatedAt?: SQLSortOrder
|
||||
orderByUpdatedAt?: SQLSortOrder
|
||||
fields?: ExtDataField[]
|
||||
}): Promise<ExtData[]> {
|
||||
const fields = v.parse(v.optional(v.array(ExtDataField), []), searchParams.fields)
|
||||
const _fields = fields ?? []
|
||||
const fields = v.parse(v.optional(v.array(ExtDataField), []), searchParams.fields)
|
||||
const _fields = fields ?? []
|
||||
|
||||
// Build the select query based on fields
|
||||
const selectQuery: SelectedFields = {
|
||||
dataId: schema.extensionData.dataId,
|
||||
extId: schema.extensionData.extId,
|
||||
dataType: schema.extensionData.dataType,
|
||||
createdAt: schema.extensionData.createdAt,
|
||||
updatedAt: schema.extensionData.updatedAt
|
||||
}
|
||||
// Build the select query based on fields
|
||||
const selectQuery: SelectedFields = {
|
||||
dataId: schema.extensionData.dataId,
|
||||
extId: schema.extensionData.extId,
|
||||
dataType: schema.extensionData.dataType,
|
||||
createdAt: schema.extensionData.createdAt,
|
||||
updatedAt: schema.extensionData.updatedAt
|
||||
}
|
||||
|
||||
if (_fields.includes("data")) {
|
||||
selectQuery["data"] = schema.extensionData.data
|
||||
}
|
||||
if (_fields.includes("search_text")) {
|
||||
selectQuery["searchText"] = schema.extensionData.searchText
|
||||
}
|
||||
if (_fields.includes("data")) {
|
||||
selectQuery["data"] = schema.extensionData.data
|
||||
}
|
||||
if (_fields.includes("search_text")) {
|
||||
selectQuery["searchText"] = schema.extensionData.searchText
|
||||
}
|
||||
|
||||
// Build the query
|
||||
let baseQuery = db.select(selectQuery).from(schema.extensionData)
|
||||
// Build the query
|
||||
let baseQuery = db.select(selectQuery).from(schema.extensionData)
|
||||
|
||||
// Add FTS join if needed
|
||||
if (searchParams.searchMode === SearchModeEnum.FTS && searchParams.searchText) {
|
||||
// @ts-expect-error - The join type is correct but TypeScript can't infer it properly
|
||||
baseQuery = baseQuery.innerJoin(
|
||||
schema.extensionDataFts,
|
||||
orm.eq(schema.extensionData.dataId, schema.extensionDataFts.dataId)
|
||||
)
|
||||
}
|
||||
// Add FTS join if needed
|
||||
if (searchParams.searchMode === SearchModeEnum.FTS && searchParams.searchText) {
|
||||
// @ts-expect-error - The join type is correct but TypeScript can't infer it properly
|
||||
baseQuery = baseQuery.innerJoin(
|
||||
schema.extensionDataFts,
|
||||
orm.eq(schema.extensionData.dataId, schema.extensionDataFts.dataId)
|
||||
)
|
||||
}
|
||||
|
||||
// Add conditions
|
||||
const conditions = [orm.eq(schema.extensionData.extId, searchParams.extId)]
|
||||
// Add conditions
|
||||
const conditions = [orm.eq(schema.extensionData.extId, searchParams.extId)]
|
||||
|
||||
if (searchParams.dataId) {
|
||||
conditions.push(orm.eq(schema.extensionData.dataId, searchParams.dataId))
|
||||
}
|
||||
if (searchParams.dataId) {
|
||||
conditions.push(orm.eq(schema.extensionData.dataId, searchParams.dataId))
|
||||
}
|
||||
|
||||
if (searchParams.dataType) {
|
||||
conditions.push(orm.eq(schema.extensionData.dataType, searchParams.dataType))
|
||||
}
|
||||
if (searchParams.dataType) {
|
||||
conditions.push(orm.eq(schema.extensionData.dataType, searchParams.dataType))
|
||||
}
|
||||
|
||||
if (searchParams.searchText) {
|
||||
switch (searchParams.searchMode) {
|
||||
case SearchModeEnum.ExactMatch:
|
||||
conditions.push(orm.eq(schema.extensionData.searchText, searchParams.searchText))
|
||||
break
|
||||
case SearchModeEnum.Like:
|
||||
conditions.push(orm.like(schema.extensionData.searchText, `%${searchParams.searchText}%`))
|
||||
break
|
||||
case SearchModeEnum.FTS:
|
||||
conditions.push(
|
||||
orm.sql`${schema.extensionDataFts.searchText} MATCH ${searchParams.searchText}`
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (searchParams.searchText) {
|
||||
switch (searchParams.searchMode) {
|
||||
case SearchModeEnum.ExactMatch:
|
||||
conditions.push(orm.eq(schema.extensionData.searchText, searchParams.searchText))
|
||||
break
|
||||
case SearchModeEnum.Like:
|
||||
conditions.push(orm.like(schema.extensionData.searchText, `%${searchParams.searchText}%`))
|
||||
break
|
||||
case SearchModeEnum.FTS:
|
||||
conditions.push(
|
||||
orm.sql`${schema.extensionDataFts.searchText} MATCH ${searchParams.searchText}`
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParams.afterCreatedAt) {
|
||||
conditions.push(orm.gt(schema.extensionData.createdAt, searchParams.afterCreatedAt))
|
||||
}
|
||||
if (searchParams.afterCreatedAt) {
|
||||
conditions.push(orm.gt(schema.extensionData.createdAt, searchParams.afterCreatedAt))
|
||||
}
|
||||
|
||||
if (searchParams.beforeCreatedAt) {
|
||||
conditions.push(orm.lt(schema.extensionData.createdAt, searchParams.beforeCreatedAt))
|
||||
}
|
||||
if (searchParams.beforeCreatedAt) {
|
||||
conditions.push(orm.lt(schema.extensionData.createdAt, searchParams.beforeCreatedAt))
|
||||
}
|
||||
|
||||
// Build the final query with all conditions and modifiers
|
||||
const query = baseQuery
|
||||
.where(orm.and(...conditions))
|
||||
.orderBy(
|
||||
searchParams.orderByCreatedAt
|
||||
? searchParams.orderByCreatedAt === SQLSortOrderEnum.Asc
|
||||
? orm.asc(schema.extensionData.createdAt)
|
||||
: orm.desc(schema.extensionData.createdAt)
|
||||
: searchParams.orderByUpdatedAt
|
||||
? searchParams.orderByUpdatedAt === SQLSortOrderEnum.Asc
|
||||
? orm.asc(schema.extensionData.updatedAt)
|
||||
: orm.desc(schema.extensionData.updatedAt)
|
||||
: orm.asc(schema.extensionData.createdAt) // Default ordering
|
||||
)
|
||||
.limit(searchParams.limit ?? 100) // Default limit
|
||||
.offset(searchParams.offset ?? 0) // Default offset
|
||||
// Build the final query with all conditions and modifiers
|
||||
const query = baseQuery
|
||||
.where(orm.and(...conditions))
|
||||
.orderBy(
|
||||
searchParams.orderByCreatedAt
|
||||
? searchParams.orderByCreatedAt === SQLSortOrderEnum.Asc
|
||||
? orm.asc(schema.extensionData.createdAt)
|
||||
: orm.desc(schema.extensionData.createdAt)
|
||||
: searchParams.orderByUpdatedAt
|
||||
? searchParams.orderByUpdatedAt === SQLSortOrderEnum.Asc
|
||||
? orm.asc(schema.extensionData.updatedAt)
|
||||
: orm.desc(schema.extensionData.updatedAt)
|
||||
: orm.asc(schema.extensionData.createdAt) // Default ordering
|
||||
)
|
||||
.limit(searchParams.limit ?? 100) // Default limit
|
||||
.offset(searchParams.offset ?? 0) // Default offset
|
||||
|
||||
// Execute query and convert results
|
||||
const results = await query.all()
|
||||
return results
|
||||
.map((rawData) => {
|
||||
// @ts-expect-error - rawData is unknown, but will be safe parsed with valibot
|
||||
return convertRawExtDataToExtData(rawData)
|
||||
})
|
||||
.filter((item): item is ExtData => item !== undefined)
|
||||
// Execute query and convert results
|
||||
const results = await query.all()
|
||||
return results
|
||||
.map((rawData) => {
|
||||
// @ts-expect-error - rawData is unknown, but will be safe parsed with valibot
|
||||
return convertRawExtDataToExtData(rawData)
|
||||
})
|
||||
.filter((item): item is ExtData => item !== undefined)
|
||||
}
|
||||
|
||||
|
||||
export function deleteExtensionDataById(dataId: number) {
|
||||
|
||||
// return invoke<void>(generateJarvisPluginCommand("delete_extension_data_by_id"), { dataId })
|
||||
return db
|
||||
.delete(schema.extensionData)
|
||||
.where(orm.eq(schema.extensionData.dataId, dataId))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
}
|
||||
|
||||
export function updateExtensionDataById(data: {
|
||||
dataId: number
|
||||
data: string
|
||||
searchText?: string
|
||||
}) {
|
||||
// return invoke<void>(generateJarvisPluginCommand("update_extension_data_by_id"), data)
|
||||
}): Promise<void> {
|
||||
return db
|
||||
.update(schema.extensionData)
|
||||
.set({
|
||||
data: data.data,
|
||||
searchText: data.searchText
|
||||
})
|
||||
.where(orm.eq(schema.extensionData.dataId, data.dataId))
|
||||
.run()
|
||||
.then(() => undefined)
|
||||
}
|
||||
|
||||
/**
|
||||
* Database API for extensions.
|
||||
* Extensions shouldn't have full access to the database, they can only access their own data.
|
||||
* When an extension is loaded, the main thread will create an instance of this class and
|
||||
* expose it to the extension.
|
||||
*/
|
||||
export class JarvisExtDB implements IDb {
|
||||
extId: number
|
||||
|
||||
// export async function getNCommands(n: number):
|
||||
// export function createExtension(ext: {
|
||||
// identifier: string
|
||||
// version: string
|
||||
// enabled?: boolean
|
||||
// path?: string
|
||||
// data?: any
|
||||
// }) {
|
||||
// return invoke<void>(generateJarvisPluginCommand("create_extension"), ext)
|
||||
// }
|
||||
constructor(extId: number) {
|
||||
this.extId = extId
|
||||
}
|
||||
|
||||
async add(data: { data: string; dataType?: string; searchText?: string }): Promise<void> {
|
||||
return createExtensionData({
|
||||
data: data.data,
|
||||
dataType: data.dataType ?? "default",
|
||||
searchText: data.searchText,
|
||||
extId: this.extId
|
||||
}).then(() => undefined)
|
||||
}
|
||||
|
||||
async delete(dataId: number): Promise<void> {
|
||||
// Verify if this data belongs to this extension
|
||||
const d = await getExtensionDataById(dataId)
|
||||
if (!d || d.extId !== this.extId) {
|
||||
throw new Error("Extension Data not found")
|
||||
}
|
||||
return await deleteExtensionDataById(dataId)
|
||||
}
|
||||
|
||||
async search(searchParams: {
|
||||
dataId?: number
|
||||
searchMode?: SearchMode
|
||||
dataType?: string
|
||||
searchText?: string
|
||||
afterCreatedAt?: Date
|
||||
beforeCreatedAt?: Date
|
||||
limit?: number
|
||||
orderByCreatedAt?: SQLSortOrder
|
||||
orderByUpdatedAt?: SQLSortOrder
|
||||
fields?: ExtDataField[]
|
||||
}): Promise<ExtData[]> {
|
||||
const beforeCreatedAt = searchParams.beforeCreatedAt
|
||||
? convertDateToSqliteString(searchParams.beforeCreatedAt)
|
||||
: undefined
|
||||
const afterCreatedAt = searchParams.afterCreatedAt
|
||||
? convertDateToSqliteString(searchParams.afterCreatedAt)
|
||||
: undefined
|
||||
return searchExtensionData({
|
||||
...searchParams,
|
||||
searchMode: searchParams.searchMode ?? SearchModeEnum.FTS,
|
||||
extId: this.extId,
|
||||
beforeCreatedAt,
|
||||
afterCreatedAt
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all data of this extension.
|
||||
* Use `search()` method for more advanced search.
|
||||
* @param options optional fields to retrieve. By default, data and searchText are not returned.
|
||||
* @returns
|
||||
*/
|
||||
retrieveAll(options: { fields?: ExtDataField[] }): Promise<ExtData[]> {
|
||||
return this.search({ fields: options.fields })
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all data of this extension by type.
|
||||
* Use `search()` method for more advanced search.
|
||||
* @param dataType
|
||||
* @returns
|
||||
*/
|
||||
retrieveAllByType(dataType: string): Promise<ExtData[]> {
|
||||
return this.search({ dataType })
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all data of this extension.
|
||||
*/
|
||||
deleteAll(): Promise<void> {
|
||||
return this.search({})
|
||||
.then((items) => {
|
||||
return Promise.all(items.map((item) => this.delete(item.dataId)))
|
||||
})
|
||||
.then(() => {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data and searchText of this extension.
|
||||
* @param dataId unique id of the data
|
||||
* @param data
|
||||
* @param searchText
|
||||
* @returns
|
||||
*/
|
||||
async update(data: { dataId: number; data: string; searchText?: string }): Promise<void> {
|
||||
const d = await getExtensionDataById(data.dataId)
|
||||
if (!d || d.extId !== this.extId) {
|
||||
throw new Error("Extension Data not found")
|
||||
}
|
||||
return updateExtensionDataById(data)
|
||||
}
|
||||
}
|
||||
|
||||
export class KV implements IKV {
|
||||
extId: number
|
||||
db: JarvisExtDB
|
||||
private DataType: string = "kunkun_kv"
|
||||
|
||||
constructor(extId: number) {
|
||||
this.extId = extId
|
||||
this.db = new JarvisExtDB(extId)
|
||||
}
|
||||
|
||||
get<T = string>(key: string): Promise<T | null | undefined> {
|
||||
return this.db
|
||||
.search({
|
||||
dataType: this.DataType,
|
||||
searchText: key,
|
||||
searchMode: SearchModeEnum.ExactMatch,
|
||||
fields: ["search_text", "data"]
|
||||
})
|
||||
.then((items) => {
|
||||
if (items.length === 0) {
|
||||
return null
|
||||
} else if (items.length > 1) {
|
||||
throw new Error("Multiple KVs with the same key")
|
||||
}
|
||||
return items[0]?.data ? (JSON.parse(items[0].data).value as T) : null
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn(err)
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
set(key: string, value: string): Promise<void> {
|
||||
return this.db
|
||||
.search({
|
||||
dataType: this.DataType,
|
||||
searchText: key,
|
||||
searchMode: SearchModeEnum.ExactMatch
|
||||
})
|
||||
.then((items) => {
|
||||
if (items.length === 0) {
|
||||
return this.db.add({
|
||||
data: JSON.stringify({ value: value }),
|
||||
dataType: this.DataType,
|
||||
searchText: key
|
||||
})
|
||||
} else if (items.length === 1) {
|
||||
return this.db.update({
|
||||
dataId: items[0]!.dataId,
|
||||
data: JSON.stringify({ value: value }),
|
||||
searchText: key
|
||||
})
|
||||
} else {
|
||||
return Promise.all(items.map((item) => this.db.delete(item.dataId))).then(() =>
|
||||
Promise.resolve()
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
delete(key: string): Promise<void> {
|
||||
return this.db
|
||||
.search({
|
||||
dataType: this.DataType,
|
||||
searchText: key,
|
||||
searchMode: SearchModeEnum.ExactMatch
|
||||
})
|
||||
.then((items) => {
|
||||
return Promise.all(items.map((item) => this.db.delete(item.dataId))).then(() =>
|
||||
Promise.resolve()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
exists(key: string): Promise<boolean> {
|
||||
return this.db
|
||||
.search({
|
||||
dataType: this.DataType,
|
||||
searchText: key,
|
||||
searchMode: SearchModeEnum.ExactMatch,
|
||||
fields: []
|
||||
})
|
||||
.then((items) => {
|
||||
return items.length > 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,47 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { db as dbCmd } from "@kksh/api/commands"
|
||||
import * as schema from "../drizzle/schema"
|
||||
import { sql } from "@kksh/api/commands"
|
||||
import { error } from "@tauri-apps/plugin-log"
|
||||
import { drizzle } from "drizzle-orm/sqlite-proxy"
|
||||
|
||||
/**
|
||||
* Loads the sqlite database via the Tauri Proxy.
|
||||
*/
|
||||
// export const sqlite = await Database.load("sqlite:test.db");
|
||||
import * as schema from "../drizzle/schema"
|
||||
|
||||
/**
|
||||
* The drizzle database instance.
|
||||
*/
|
||||
export const db = drizzle<typeof schema>(
|
||||
async (sql, params, method) => {
|
||||
let rows: any = []
|
||||
let results = []
|
||||
console.log({
|
||||
sql,
|
||||
params,
|
||||
method
|
||||
})
|
||||
console.log(sql)
|
||||
// If the query is a SELECT, use the select method
|
||||
if (isSelectQuery(sql)) {
|
||||
rows = await dbCmd.select(sql, params).catch((e) => {
|
||||
error("SQL Error:", e)
|
||||
return []
|
||||
})
|
||||
} else {
|
||||
// Otherwise, use the execute method
|
||||
rows = await dbCmd.execute(sql, params).catch((e) => {
|
||||
error("SQL Error:", e)
|
||||
return []
|
||||
})
|
||||
return { rows: [] }
|
||||
}
|
||||
async (sqlQuery, params, method) => {
|
||||
let rows: any = []
|
||||
let results = []
|
||||
console.log({
|
||||
sql: sqlQuery,
|
||||
params,
|
||||
method
|
||||
})
|
||||
console.log(sqlQuery)
|
||||
// If the query is a SELECT, use the select method
|
||||
if (isSelectQuery(sqlQuery)) {
|
||||
rows = await sql.select(sqlQuery, params).catch((e) => {
|
||||
error("SQL Error:", e)
|
||||
return []
|
||||
})
|
||||
} else {
|
||||
// Otherwise, use the execute method
|
||||
rows = await sql.execute(sqlQuery, params).catch((e) => {
|
||||
error("SQL Error:", e)
|
||||
return []
|
||||
})
|
||||
return { rows: [] }
|
||||
}
|
||||
|
||||
rows = rows.map((row: any) => {
|
||||
return Object.values(row)
|
||||
})
|
||||
rows = rows.map((row: any) => {
|
||||
return Object.values(row)
|
||||
})
|
||||
|
||||
// If the method is "all", return all rows
|
||||
results = method === "all" ? rows : rows[0]
|
||||
return { rows: results }
|
||||
},
|
||||
// Pass the schema to the drizzle instance
|
||||
{ schema: schema, logger: true }
|
||||
// If the method is "all", return all rows
|
||||
results = method === "all" ? rows : rows[0]
|
||||
return { rows: results }
|
||||
},
|
||||
// Pass the schema to the drizzle instance
|
||||
{ schema: schema, logger: true }
|
||||
)
|
||||
|
||||
/**
|
||||
@ -55,6 +50,6 @@ export const db = drizzle<typeof schema>(
|
||||
* @returns True if the query is a SELECT query, false otherwise.
|
||||
*/
|
||||
function isSelectQuery(sql: string): boolean {
|
||||
const selectRegex = /^\s*SELECT\b/i
|
||||
return selectRegex.test(sql)
|
||||
const selectRegex = /^\s*SELECT\b/i
|
||||
return selectRegex.test(sql)
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@kksh/api": "workspace:*",
|
||||
"@kksh/drizzle": "workspace:*",
|
||||
"@std/semver": "npm:@jsr/std__semver@^1.0.4",
|
||||
"@tauri-apps/plugin-upload": "^2.2.1",
|
||||
"semver": "^7.7.1",
|
||||
|
@ -1,14 +1,6 @@
|
||||
import { db } from "@kksh/api/commands"
|
||||
import {
|
||||
CmdTypeEnum,
|
||||
ExtCmd,
|
||||
ExtPackageJson,
|
||||
ExtPackageJsonExtra,
|
||||
Icon,
|
||||
QuickLinkCmd
|
||||
} from "@kksh/api/models"
|
||||
import { CmdTypeEnum, ExtPackageJson, Icon, QuickLinkCmd } from "@kksh/api/models"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import * as v from "valibot"
|
||||
import { isExtPathInDev } from "./utils"
|
||||
|
||||
export async function upsertExtension(extPkgJson: ExtPackageJson, extFullPath: string) {
|
||||
const extInDb = await db.getUniqueExtensionByIdentifier(extPkgJson.kunkun.identifier)
|
||||
@ -39,7 +31,7 @@ export async function createQuickLinkCommand(name: string, link: string, icon: I
|
||||
export async function getAllQuickLinkCommands(): Promise<QuickLinkCmd[]> {
|
||||
const extension = await db.getExtQuickLinks()
|
||||
const cmds = await db.getCommandsByExtId(extension.extId)
|
||||
return cmds
|
||||
const parsedCmds = cmds
|
||||
.map((cmd) => {
|
||||
try {
|
||||
cmd.data = JSON.parse(cmd.data)
|
||||
@ -55,4 +47,5 @@ export async function getAllQuickLinkCommands(): Promise<QuickLinkCmd[]> {
|
||||
}
|
||||
})
|
||||
.filter((cmd) => cmd !== null)
|
||||
return parsedCmds
|
||||
}
|
||||
|
@ -3,8 +3,9 @@
|
||||
* including install, uninstall, upgrade, check app-extension compatibility, etc.
|
||||
*/
|
||||
import { isCompatible } from "@kksh/api"
|
||||
import { copy_dir_all, db, decompressTarball } from "@kksh/api/commands"
|
||||
import { copy_dir_all, decompressTarball } from "@kksh/api/commands"
|
||||
import type { ExtensionStoreListItem, ExtPackageJsonExtra } from "@kksh/api/models"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { greaterThan, parse as parseSemver } from "@std/semver"
|
||||
import * as path from "@tauri-apps/api/path"
|
||||
import * as dialog from "@tauri-apps/plugin-dialog"
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { db } from "@kksh/api/commands"
|
||||
import { ExtPackageJson, ExtPackageJsonExtra, License } from "@kksh/api/models"
|
||||
import { db } from "@kksh/drizzle"
|
||||
import { basename, dirname, join } from "@tauri-apps/api/path"
|
||||
import { readDir, readTextFile } from "@tauri-apps/plugin-fs"
|
||||
import { debug, error } from "@tauri-apps/plugin-log"
|
||||
@ -77,7 +77,7 @@ export function loadAllExtensionsFromDisk(
|
||||
* @returns loaded extensions
|
||||
*/
|
||||
export async function loadAllExtensionsFromDb(): Promise<ExtPackageJsonExtra[]> {
|
||||
const allDbExts = await (await db.getAllExtensions()).filter((ext) => ext.path)
|
||||
const allDbExts = (await db.getAllExtensions()).filter((ext) => ext.path)
|
||||
const results: ExtPackageJsonExtra[] = []
|
||||
for (const ext of allDbExts) {
|
||||
if (!ext.path) continue
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
## Permission Table
|
||||
|
||||
<table>
|
||||
@ -7,7 +6,6 @@
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
|
@ -1,11 +1,9 @@
|
||||
<script lang="ts">
|
||||
import autoAnimate from "@formkit/auto-animate"
|
||||
import Icon from "@iconify/svelte"
|
||||
import { Button, ButtonModule, Collapsible, ScrollArea } from "@kksh/svelte5"
|
||||
import { Error, Layouts, Shiki } from "@kksh/ui"
|
||||
import { ChevronsUpDown } from "lucide-svelte"
|
||||
import { type Snippet } from "svelte"
|
||||
import { fade, slide } from "svelte/transition"
|
||||
|
||||
const {
|
||||
title,
|
||||
|
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@ -588,6 +588,9 @@ importers:
|
||||
'@kksh/api':
|
||||
specifier: workspace:*
|
||||
version: link:../api
|
||||
'@kksh/drizzle':
|
||||
specifier: workspace:*
|
||||
version: link:../drizzle
|
||||
'@std/semver':
|
||||
specifier: npm:@jsr/std__semver@^1.0.4
|
||||
version: '@jsr/std__semver@1.0.3'
|
||||
|
Loading…
x
Reference in New Issue
Block a user