Initial import of LogDarioSeq baseline

Establishes version control for the personal Logseq DB fork before
starting the server-client architecture migration and domain features
described in docs/agent-guide and the approved implementation plan.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 15:32:15 +02:00
commit 40631362b3
2016 changed files with 543898 additions and 0 deletions

View File

@@ -0,0 +1,283 @@
import { LSPluginUser } from '../LSPlugin.user'
import { PluginLocal } from '../LSPlugin.core'
import { safeSnakeCase } from '../common'
/**
* Declarative condition for matching a block's properties map.
* Operators: has, equals, in, not, any, all.
*/
export type BlockPropertiesCondition =
| { has: string }
| { equals: [string, any] }
| { in: [string, Array<any>] }
| { not: BlockPropertiesCondition }
| { any: Array<BlockPropertiesCondition> }
| { all: Array<BlockPropertiesCondition> }
export type BlockPropertiesRendererProps = {
blockId: string
properties: Record<string, any>
}
export type BlockRendererChild = Record<string, any> & {
children?: Array<BlockRendererChild>
}
export type BlockRendererProps = BlockPropertiesRendererProps & {
uuid?: string
page?: string
content?: string
format?: string
children?: Array<BlockRendererChild>
}
export type BlockPropertiesPredicate = (
props: BlockPropertiesRendererProps
) => boolean
export type BlockRendererPredicate = (
props: BlockRendererProps
) => boolean
/**
* WARN: These are some experience features and might be adjusted at any time.
* These unofficial plugins that use these APIs are temporarily
* may not be supported on the Marketplace.
*/
export class LSPluginExperiments {
constructor(private ctx: LSPluginUser) {}
get React(): unknown {
return this.ensureHostScope().React
}
get ReactDOM(): unknown {
return this.ensureHostScope().ReactDOM
}
get Components() {
const exper = this.ensureHostScope().logseq.sdk.experiments
return {
Editor: exper.cp_page_editor as (props: { page: string } & any) => any,
}
}
get Utils() {
const utils = this.ensureHostScope().logseq.sdk.utils
const withCall = (name: string): ((input: any) => any) =>
utils[safeSnakeCase(name)]
return {
toClj: withCall('toClj'),
jsxToClj: withCall('jsxToClj'),
toJs: withCall('toJs'),
toKeyword: withCall('toKeyword'),
toSymbol: withCall('toSymbol'),
}
}
get pluginLocal(): PluginLocal {
return this.ensureHostScope().LSPluginCore.ensurePlugin(
this.ctx.baseInfo.id
)
}
public invokeExperMethod(type: string, ...args: Array<any>) {
const host = this.ensureHostScope()
type = safeSnakeCase(type)?.toLowerCase()
const fn =
host.logseq.api['exper_' + type] || host.logseq.sdk.experiments[type]
return fn?.apply(host, args)
}
async loadScripts(...scripts: Array<string>) {
scripts = scripts.map((it) => {
if (!it?.startsWith('http')) {
return this.ctx.resolveResourceFullUrl(it)
}
return it
})
scripts.unshift(this.ctx.baseInfo.id)
await this.invokeExperMethod('loadScripts', ...scripts)
}
registerFencedCodeRenderer(
lang: string,
opts: {
edit?: boolean
before?: () => Promise<void>
subs?: Array<string>
render: (props: { content: string }) => any
}
) {
return this.invokeExperMethod(
'registerFencedCodeRenderer',
this.ctx.baseInfo.id,
lang,
opts
)
}
registerDaemonRenderer(
key: string,
opts: {
before?: () => Promise<void>
subs?: Array<string>
render: (props: {}) => any
}
) {
return this.invokeExperMethod(
'registerDaemonRenderer',
this.ctx.baseInfo.id,
key,
opts
)
}
registerHostedRenderer(
key: string,
opts: {
title?: string
subs?: Array<string>
type?: string
render: (props: {}) => any
}
) {
return this.invokeExperMethod(
'registerHostedRenderer',
this.ctx.baseInfo.id,
key,
opts
)
}
registerSidebarRenderer(
key: string,
opts: {
title?: string
subs?: Array<string>
render: (props: {}) => any
[k: string]: any
}
) {
key = `_sidebar.${key}`
opts.type = 'sidebar'
return this.registerHostedRenderer(key, opts)
}
registerRouteRenderer(
key: string,
opts: {
name?: string
subs?: Array<string>
path: string
render: (props: {}) => any
}
) {
return this.invokeExperMethod(
'registerRouteRenderer',
this.ctx.baseInfo.id,
key,
opts
)
}
/**
* Register a custom renderer for the block properties area.
* The renderer is shown when the block's properties match the `when` condition.
* `when` may be either a declarative condition object or a synchronous predicate.
*
* @param key Unique key for this renderer (scoped to the plugin).
* @param opts Renderer options.
* @param opts.when Optional condition or synchronous predicate; if omitted, always matches.
* @param opts.mode "prepend" | "append" (default) | "replace".
* @param opts.priority Higher number wins when multiple replace renderers match.
* @param opts.subs Reserved subscription list for future reactive updates.
* @param opts.render React function component receiving `{ blockId, properties }`.
*/
registerBlockPropertiesRenderer(
key: string,
opts: {
when?: BlockPropertiesCondition | BlockPropertiesPredicate
mode?: 'prepend' | 'append' | 'replace'
priority?: number
subs?: Array<string>
render: (props: BlockPropertiesRendererProps) => any
}
) {
return this.invokeExperMethod(
'registerBlockPropertiesRenderer',
this.ctx.baseInfo.id,
key,
opts
)
}
/**
* Register a custom renderer for the block body.
* When the synchronous predicate matches, the plugin renderer replaces the
* default outline view by default. Users can switch back to outline view via
* an explicit UI toggle on each matched block.
*
* @param key Unique key for this renderer (scoped to the plugin).
* @param opts Renderer options.
* @param opts.when Optional synchronous predicate; if omitted, always matches.
* @param opts.includeChildren When true, passes the block's recursive children
* tree to the renderer and hides native outline children while the plugin
* renderer is active.
* @param opts.priority Higher number wins when multiple block renderers match.
* @param opts.subs Reserved subscription list for future reactive updates.
* @param opts.render React function component receiving block renderer props.
*/
registerBlockRenderer(
key: string,
opts: {
when?: BlockRendererPredicate
includeChildren?: boolean
priority?: number
subs?: Array<string>
render: (props: BlockRendererProps) => any
}
) {
return this.invokeExperMethod(
'registerBlockRenderer',
this.ctx.baseInfo.id,
key,
opts
)
}
registerExtensionsEnhancer<T = any>(
type: 'katex' | 'codemirror',
enhancer: (v: T) => Promise<any>
) {
const host = this.ensureHostScope()
switch (type) {
case 'katex':
if (host.katex) {
enhancer(host.katex).catch(console.error)
}
break
default:
}
return this.invokeExperMethod(
'registerExtensionsEnhancer',
this.ctx.baseInfo.id,
type,
enhancer
)
}
ensureHostScope(): any {
try {
window.top?.document
} catch (_e) {
console.error('Can not access host scope!')
}
return window.top
}
}

View File

@@ -0,0 +1,670 @@
import { LSPluginUser } from '../LSPlugin.user'
export type NetMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD'
export type NetResponseType =
| 'json'
| 'text'
| 'base64'
| 'arraybuffer'
| 'arrayBuffer'
export type NetHeaders = Record<string, string>
export type NetRetryOptions = {
retries?: number
delay?: number
factor?: number
retryOn?:
| Array<number>
| ((response: LSPluginNetResponse<any> | Error) => boolean)
}
export type NetCacheOptions = {
key?: string
ttl?: number
maxEntries?: number
}
export type NetRequestOptions<TBody = any> = {
url: string
method?: NetMethod
headers?: NetHeaders
body?: TBody | string | ArrayBuffer
data?: TBody | string | ArrayBuffer
responseType?: NetResponseType
timeout?: number
retry?: number | NetRetryOptions
cache?: boolean | NetCacheOptions
signal?: AbortSignal
abortable?: boolean
}
export type NetRequestConfig<TBody = any> = Omit<
NetRequestOptions<TBody>,
'url' | 'method'
>
export type NetRequestBody<TBody = any> = TBody | string | ArrayBuffer
export type NetResponsePayload<T = any> = {
status: number
statusText: string
ok: boolean
url: string
headers: NetHeaders
body: T
}
type NormalizedRetryOptions = Required<
Pick<NetRetryOptions, 'retries' | 'delay' | 'factor'>
> &
Pick<NetRetryOptions, 'retryOn'>
type CacheEntry<T = any> = {
expires: number
response: LSPluginNetResponse<T>
}
type HostResponseType = 'json' | 'text' | 'base64' | 'arraybuffer'
type HostRequestID = string | number
const DEFAULT_CACHE_TTL = 5 * 60 * 1000
const DEFAULT_CACHE_MAX_ENTRIES = 100
const CLIENT_MSG_CALLBACK = '#lsp#request#callback'
const genTaskCallbackType = (id: HostRequestID) => `task_callback_${id}`
function normalizeResponseType(type?: NetResponseType): HostResponseType {
return type === 'arrayBuffer' ? 'arraybuffer' : type || 'json'
}
function makeAbortError() {
const error = new Error('The request was aborted')
error.name = 'AbortError'
return error
}
function wait(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
function responseToCachePayload<T>(
response: LSPluginNetResponse<T>
): NetResponsePayload<T> {
return {
status: response.status,
statusText: response.statusText,
ok: response.ok,
url: response.url,
headers: { ...response.headers },
body: response.body,
}
}
function encodeText(value: string) {
return new TextEncoder().encode(value).buffer
}
function decodeText(value: ArrayBuffer) {
return new TextDecoder().decode(value)
}
function base64ToArrayBuffer(value: string) {
const binary = atob(value)
const payload = new Uint8Array(binary.length)
for (let i = 0; i < binary.length; i++) {
payload[i] = binary.charCodeAt(i)
}
return payload.buffer
}
function arrayBufferToBase64(value: ArrayBuffer) {
const payload = new Uint8Array(value)
const chunkSize = 0x8000
let binary = ''
for (let i = 0; i < payload.length; i += chunkSize) {
binary += String.fromCharCode(...payload.subarray(i, i + chunkSize))
}
return btoa(binary)
}
function headersToRecord(headers: Headers) {
const result: NetHeaders = {}
headers.forEach((value, key) => {
result[key] = value
})
return result
}
function bodyToFetchBody(value: unknown): BodyInit | undefined {
if (value == null) return undefined
if (typeof value === 'string') return value
if (value instanceof ArrayBuffer) return value
if (ArrayBuffer.isView(value)) return value as BodyInit
return JSON.stringify(value)
}
export class LSPluginNetResponse<T = any> {
constructor(
private _payload: NetResponsePayload<T>,
private _responseType: HostResponseType
) {}
get status() {
return this._payload.status
}
get statusText() {
return this._payload.statusText
}
get ok() {
return this._payload.ok
}
get url() {
return this._payload.url
}
get headers() {
return this._payload.headers
}
get body() {
return this._payload.body
}
async json<R = any>(): Promise<R> {
const { body } = this._payload
if (this._responseType === 'json') {
return body as unknown as R
}
if (typeof body === 'string') {
return JSON.parse(body) as R
}
if (body instanceof ArrayBuffer) {
return JSON.parse(decodeText(body)) as R
}
return body as unknown as R
}
async text(): Promise<string> {
const { body } = this._payload
if (typeof body === 'string') {
return body
}
if (body instanceof ArrayBuffer) {
return decodeText(body)
}
return JSON.stringify(body)
}
async arrayBuffer(): Promise<ArrayBuffer> {
const { body } = this._payload
if (body instanceof ArrayBuffer) {
return body
}
if (this._responseType === 'base64' && typeof body === 'string') {
return base64ToArrayBuffer(body)
}
if (typeof body === 'string') {
return encodeText(body)
}
return encodeText(JSON.stringify(body))
}
clone(): LSPluginNetResponse<T> {
return new LSPluginNetResponse(
responseToCachePayload(this),
this._responseType
)
}
}
export class LSPluginNetError<T = any> extends Error {
constructor(public response: LSPluginNetResponse<T>) {
super(
`HTTP request failed with status ${response.status} ${response.statusText}`.trim()
)
this.name = 'LSPluginNetError'
}
get status() {
return this.response.status
}
get statusText() {
return this.response.statusText
}
get url() {
return this.response.url
}
get headers() {
return this.response.headers
}
get body() {
return this.response.body
}
}
function responseBodyOrThrow<T>(response: LSPluginNetResponse<T>) {
if (response.status >= 200 && response.status < 300) {
return response.body
}
throw new LSPluginNetError(response)
}
/**
* HTTP client for plugins. Desktop requests are proxied by the host process so
* iframe plugins can avoid browser CORS limitations. Web requests fall back to
* browser fetch and are subject to normal CORS rules.
*/
export class LSPluginNet {
private _cache = new Map<string, CacheEntry>()
private _events = new EventTarget()
constructor(private _ctx: LSPluginUser) {
this._ctx.caller.on(CLIENT_MSG_CALLBACK, (e: any) => {
const reqId = e?.requestId
if (!reqId) return
this._events.dispatchEvent(
new CustomEvent(genTaskCallbackType(reqId), { detail: e?.payload })
)
})
}
async request<T = any>(
options: NetRequestOptions
): Promise<LSPluginNetResponse<T>> {
const method = (options.method || 'GET').toUpperCase() as NetMethod
const responseType = normalizeResponseType(options.responseType)
const retry = this.normalizeRetry(options.retry)
const cacheKey = this.getCacheKey(options, method, responseType)
if (cacheKey) {
const cached = this._cache.get(cacheKey)
if (cached && cached.expires > Date.now()) {
this._cache.delete(cacheKey)
this._cache.set(cacheKey, cached)
return cached.response.clone()
}
cached && this._cache.delete(cacheKey)
}
let attempt = 0
while (true) {
try {
const response = await this.performRequest<T>(
options,
method,
responseType
)
if (attempt < retry.retries && this.shouldRetry(retry, response)) {
await wait(retry.delay * Math.pow(retry.factor, attempt++))
continue
}
if (cacheKey) {
this.pruneCache(this.getCacheMaxEntries(options.cache))
this._cache.set(cacheKey, {
expires: Date.now() + this.getCacheTTL(options.cache),
response: response.clone(),
})
}
return response
} catch (e: any) {
if (
options.signal?.aborted ||
attempt >= retry.retries ||
!this.shouldRetry(retry, e)
) {
throw e
}
await wait(retry.delay * Math.pow(retry.factor, attempt++))
}
}
}
get<T = any>(
url: string,
options?: NetRequestConfig
): Promise<T> {
return this.requestWithMethod<T>('GET', url, options)
}
head<T = any>(
url: string,
options?: NetRequestConfig
): Promise<T> {
return this.requestWithMethod<T>('HEAD', url, options)
}
post<T = any, TBody = any>(
url: string,
body?: NetRequestBody<TBody>,
options?: NetRequestConfig<TBody>
): Promise<T> {
return this.requestWithBody<T, TBody>('POST', url, body, options)
}
put<T = any, TBody = any>(
url: string,
body?: NetRequestBody<TBody>,
options?: NetRequestConfig<TBody>
): Promise<T> {
return this.requestWithBody<T, TBody>('PUT', url, body, options)
}
patch<T = any, TBody = any>(
url: string,
body?: NetRequestBody<TBody>,
options?: NetRequestConfig<TBody>
): Promise<T> {
return this.requestWithBody<T, TBody>('PATCH', url, body, options)
}
delete<T = any, TBody = any>(
url: string,
options?: NetRequestConfig<TBody>
): Promise<T> {
return this.requestWithMethod<T, TBody>('DELETE', url, options)
}
private requestWithMethod<T = any, TBody = any>(
method: NetMethod,
url: string,
options: NetRequestConfig<TBody> = {}
) {
return this.request<T>({
...options,
url,
method,
}).then(responseBodyOrThrow)
}
private requestWithBody<T = any, TBody = any>(
method: Extract<NetMethod, 'POST' | 'PUT' | 'PATCH'>,
url: string,
body?: NetRequestBody<TBody>,
options: NetRequestConfig<TBody> = {}
) {
const requestBody = typeof body === 'undefined' ? options.body : body
return this.request<T>({
...options,
url,
method,
body: requestBody,
}).then(responseBodyOrThrow)
}
private async performRequest<T>(
options: NetRequestOptions,
method: NetMethod,
responseType: HostResponseType
) {
if (this.shouldUseFetchFallback()) {
return this.performFetchRequest<T>(options, method, responseType)
}
try {
return await this.performProxyRequest<T>(options, method, responseType)
} catch (e) {
if (this.shouldFallbackAfterProxyError(e)) {
return this.performFetchRequest<T>(options, method, responseType)
}
throw e
}
}
private async performProxyRequest<T>(
options: NetRequestOptions,
method: NetMethod,
responseType: HostResponseType
) {
if (options.signal?.aborted) {
throw makeAbortError()
}
const abortable = Boolean(options.abortable || options.signal)
const reqId = await this._ctx._execCallableAPIAsync(
'exper_request',
this._ctx.baseInfo.id,
{
url: options.url,
method,
headers: options.headers,
data: options.body ?? options.data,
timeout: options.timeout,
returnType: responseType,
abortable,
includeResponse: true,
}
)
if (!reqId) {
throw new Error('Host HTTP request proxy is not available')
}
const callbackType = genTaskCallbackType(reqId)
const payloadPromise = new Promise<NetResponsePayload<T>>(
(resolve, reject) => {
const listener = (event: Event) => {
const payload = (event as CustomEvent).detail
this._events.removeEventListener(callbackType, listener)
if (payload && payload instanceof Error) {
reject(payload)
} else {
resolve(payload)
}
}
this._events.addEventListener(callbackType, listener, { once: true })
}
)
const abort = () => this.abortProxyRequest(reqId, abortable)
options.signal?.addEventListener('abort', abort, { once: true })
try {
const payload = await payloadPromise
return new LSPluginNetResponse(payload, responseType)
} finally {
options.signal?.removeEventListener('abort', abort)
}
}
private abortProxyRequest(reqId: HostRequestID, abortable: boolean) {
if (!abortable) return
this._ctx._execCallableAPI('http_request_abort', reqId)
}
private async performFetchRequest<T>(
options: NetRequestOptions,
method: NetMethod,
responseType: HostResponseType
) {
if (typeof fetch !== 'function') {
throw new Error('Browser fetch is not available')
}
if (options.signal?.aborted) {
throw makeAbortError()
}
const timeout =
typeof options.timeout === 'number' && options.timeout > 0
? options.timeout
: undefined
const controller = timeout || options.signal ? new AbortController() : null
const abort = () => controller?.abort()
const timeoutId = timeout ? setTimeout(abort, timeout) : null
options.signal?.addEventListener('abort', abort, { once: true })
try {
const response = await fetch(options.url, {
method,
headers: options.headers,
body: ['GET', 'HEAD'].includes(method)
? undefined
: bodyToFetchBody(options.body ?? options.data),
signal: controller?.signal || options.signal,
})
const body = await this.readFetchBody<T>(response, method, responseType)
return new LSPluginNetResponse<T>(
{
status: response.status,
statusText: response.statusText,
ok: response.ok,
url: response.url,
headers: headersToRecord(response.headers),
body,
},
responseType
)
} finally {
if (timeoutId) clearTimeout(timeoutId)
options.signal?.removeEventListener('abort', abort)
}
}
private async readFetchBody<T>(
response: Response,
method: NetMethod,
responseType: HostResponseType
) {
if (method === 'HEAD' || response.status === 204 || response.status === 205) {
return null as T
}
switch (responseType) {
case 'json':
return (await response.json()) as T
case 'text':
return (await response.text()) as T
case 'arraybuffer':
return (await response.arrayBuffer()) as T
case 'base64':
return arrayBufferToBase64(await response.arrayBuffer()) as T
}
}
private shouldUseFetchFallback() {
return Boolean((this._ctx.baseInfo as any)?.webMode)
}
private shouldFallbackAfterProxyError(error: unknown) {
return error == null && typeof fetch === 'function'
}
private normalizeRetry(
retry?: number | NetRetryOptions
): NormalizedRetryOptions {
if (typeof retry === 'number') {
return { retries: Math.max(0, retry), delay: 300, factor: 2 }
}
return {
retries: Math.max(0, retry?.retries || 0),
delay: retry?.delay || 300,
factor: retry?.factor || 2,
retryOn: retry?.retryOn,
}
}
private shouldRetry(
retry: NormalizedRetryOptions,
value: LSPluginNetResponse | Error
) {
const { retryOn } = retry
if (typeof retryOn === 'function') {
return retryOn(value)
}
if (value instanceof LSPluginNetResponse) {
return Array.isArray(retryOn)
? retryOn.includes(value.status)
: value.status === 429 || value.status >= 500
}
return true
}
private getCacheKey(
options: NetRequestOptions,
method: NetMethod,
responseType: HostResponseType
) {
if (!options.cache || !['GET', 'HEAD'].includes(method)) return null
if (typeof options.cache === 'object' && options.cache.key) {
return options.cache.key
}
return JSON.stringify({
method,
url: options.url,
headers: options.headers || {},
responseType,
})
}
private getCacheTTL(cache: NetRequestOptions['cache']) {
return typeof cache === 'object' && typeof cache.ttl === 'number'
? Math.max(0, cache.ttl)
: DEFAULT_CACHE_TTL
}
private getCacheMaxEntries(cache: NetRequestOptions['cache']) {
return typeof cache === 'object' && typeof cache.maxEntries === 'number'
? Math.max(1, cache.maxEntries)
: DEFAULT_CACHE_MAX_ENTRIES
}
private pruneCache(maxEntries: number) {
while (this._cache.size >= maxEntries) {
const oldest = this._cache.keys().next().value
if (!oldest) return
this._cache.delete(oldest)
}
}
get ctx(): LSPluginUser {
return this._ctx
}
}

View File

@@ -0,0 +1,79 @@
import { IPluginSearchServiceHooks } from '../LSPlugin'
import { LSPluginUser } from '../LSPlugin.user'
import { isArray, isFunction, mapKeys } from 'lodash-es'
export class LSPluginSearchService {
/**
* @param ctx
* @param serviceHooks
*/
constructor(
private ctx: LSPluginUser,
private serviceHooks: IPluginSearchServiceHooks
) {
ctx._execCallableAPI(
'register-search-service',
ctx.baseInfo.id,
serviceHooks.name,
serviceHooks.options
)
// hook events TODO: remove listeners
const wrapHookEvent = (k) => `service:search:${k}:${serviceHooks.name}`
Object.entries({
query: {
f: 'onQuery',
args: ['graph', 'q', true],
reply: true,
transformOutput: (data: any) => {
// TODO: transform keys?
if (isArray(data?.blocks)) {
data.blocks = data.blocks.map((it) => {
return it && mapKeys(it, (_, k) => `block/${k}`)
})
}
return data
},
},
rebuildBlocksIndice: { f: 'onIndiceInit', args: ['graph', 'blocks'] },
transactBlocks: { f: 'onBlocksChanged', args: ['graph', 'data'] },
truncateBlocks: { f: 'onIndiceReset', args: ['graph'] },
removeDb: { f: 'onGraph', args: ['graph'] },
}).forEach(([k, v]) => {
const hookEvent = wrapHookEvent(k)
ctx.caller.on(hookEvent, async (payload: any) => {
if (isFunction(serviceHooks?.[v.f])) {
let ret = null
try {
ret = await serviceHooks[v.f].apply(
serviceHooks,
(v.args || []).map((prop: any) => {
if (!payload) return
if (prop === true) return payload
if (payload.hasOwnProperty(prop)) {
const ret = payload[prop]
delete payload[prop]
return ret
}
})
)
if (v.transformOutput) {
ret = v.transformOutput(ret)
}
} catch (e) {
console.error('[SearchService] ', e)
ret = e
} finally {
if (v.reply) {
ctx.caller.call(`${hookEvent}:reply`, ret)
}
}
}
})
})
}
}

View File

@@ -0,0 +1,101 @@
import { LSPluginUser } from '../LSPlugin.user'
export interface IAsyncStorage {
getItem(key: string): Promise<string | undefined>
setItem(key: string, value: string): Promise<void>
removeItem(key: string): Promise<void>
hasItem(key: string): Promise<boolean>
allKeys(): Promise<Array<string>>
clear(): Promise<void>
}
/**
* A storage based on local files under specific context
*/
class LSPluginFileStorage implements IAsyncStorage {
/**
* @param ctx
* @param opts
*/
constructor(
private ctx: LSPluginUser,
private opts?: {
assets: boolean
}
) {}
/**
* plugin id
*/
get ctxId() {
return this.ctx.baseInfo.id
}
/**
* @param key A string as file name that support nested directory
* @param value Storage value
*/
setItem(key: string, value: string | any): Promise<void> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'write-plugin-storage-file',
args: [this.ctxId, key, value, this.opts?.assets],
})
}
/**
* @param key
*/
getItem(key: string): Promise<string | any> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'read-plugin-storage-file',
args: [this.ctxId, key, this.opts?.assets],
})
}
/**
* @param key
*/
removeItem(key: string): Promise<void> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'unlink-plugin-storage-file',
args: [this.ctxId, key, this.opts?.assets],
})
}
/**
* Get all path file keys
*/
allKeys(): Promise<Array<string>> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'list-plugin-storage-files',
args: [this.ctxId, this.opts?.assets],
})
}
/**
* Clears the storage
*/
clear(): Promise<void> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'clear-plugin-storage-files',
args: [this.ctxId, this.opts?.assets],
})
}
/**
* @param key
*/
hasItem(key: string): Promise<boolean> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'exist-plugin-storage-file',
args: [this.ctxId, key, this.opts?.assets],
})
}
}
export { LSPluginFileStorage }