diff --git a/packages/cli/src/addons/watcher.ts b/packages/cli/src/addons/watcher.ts index 1ee1e52dd7..e6836d9732 100644 --- a/packages/cli/src/addons/watcher.ts +++ b/packages/cli/src/addons/watcher.ts @@ -216,7 +216,7 @@ export default class FileWatcher extends Service { // attempt to load entry files const attempts = {} try { - for (const [_, filename] of reloads) { + for (const [, filename] of reloads) { attempts[filename] = unwrap(require(filename)) } } catch (err) { diff --git a/plugins/frontend/commands/src/service.ts b/plugins/frontend/commands/src/service.ts index cd181c0dfb..f1080dd80a 100644 --- a/plugins/frontend/commands/src/service.ts +++ b/plugins/frontend/commands/src/service.ts @@ -25,7 +25,7 @@ function traverse(command: Command): CommandData { export default class CommandProvider extends DataSource { cached: CommandData[] - update = debounce(0, () => this.broadcast()) + update = debounce(0, () => this.refresh()) constructor(ctx: Context) { super(ctx, 'commands') diff --git a/plugins/frontend/console/client/client.ts b/plugins/frontend/console/client/client.ts index 9b104a2d69..f4bbe69e7b 100644 --- a/plugins/frontend/console/client/client.ts +++ b/plugins/frontend/console/client/client.ts @@ -40,6 +40,14 @@ receive('data', ({ key, value }) => { store[key] = value }) +receive('patch', ({ key, value }) => { + if (Array.isArray(store[key])) { + store[key].push(...value) + } else { + Object.assign(store[key], value) + } +}) + receive('response', ({ id, value }) => { const callback = responseHooks[id] delete responseHooks[id] diff --git a/plugins/frontend/console/src/index.ts b/plugins/frontend/console/src/index.ts index 34c025af01..8902f9466e 100644 --- a/plugins/frontend/console/src/index.ts +++ b/plugins/frontend/console/src/index.ts @@ -41,11 +41,16 @@ export abstract class DataSource { }) } - async broadcast(value?: T) { - this.ctx.console.broadcast('data', { - key: this.name, - value: value || await this.get(true), - }) + protected broadcast(type: string, value: any) { + this.ctx.console.broadcast(type, { key: this.name, value }) + } + + async refresh() { + this.broadcast('data', await this.get(true)) + } + + patch(value: T) { + this.broadcast('patch', value) } } diff --git a/plugins/frontend/insight/src/index.ts b/plugins/frontend/insight/src/index.ts index 7160a0acbe..e13673fd0f 100644 --- a/plugins/frontend/insight/src/index.ts +++ b/plugins/frontend/insight/src/index.ts @@ -1,6 +1,7 @@ import { camelize, capitalize, Context, Dict, Plugin } from 'koishi' import { debounce } from 'throttle-debounce' import { DataSource } from '@koishijs/plugin-console' +import { resolve } from 'path' declare module '@koishijs/plugin-console' { interface Sources { @@ -12,15 +13,13 @@ export default class RegistryProvider extends DataSource> { static using = ['console'] as const private cache: Dict - private timer = setInterval(() => this.update(), 1000) - private update = debounce(0, () => { - this.broadcast() - this.timer.refresh() - }) constructor(ctx: Context) { super(ctx, 'registry') + const filename = ctx.console.config.devMode ? '../client/index.ts' : '../dist/index.js' + ctx.console.addEntry(resolve(__dirname, filename)) + ctx.on('plugin-added', this.update) ctx.on('plugin-removed', this.update) ctx.on('service', this.update) @@ -31,6 +30,25 @@ export default class RegistryProvider extends DataSource> { clearInterval(this.timer) } + private update = debounce(0, () => { + this.timer.refresh() + this.refresh() + }) + + private timer = setInterval(() => { + if (!this.cache) return + const patch: Dict = {} + for (const [, state] of this.ctx.app.registry) { + const data = this.cache[state.id] + if (!data) continue + if (state.disposables.length !== data.disposables) { + data.disposables = state.disposables.length + patch[state.id] = data + } + } + if (Object.keys(patch).length) this.patch(patch) + }, 1000) + async get(forced = false) { if (this.cache && !forced) return this.cache this.cache = {} diff --git a/plugins/frontend/logger/client/logs.vue b/plugins/frontend/logger/client/logs.vue index e73acd3532..ed00b78756 100644 --- a/plugins/frontend/logger/client/logs.vue +++ b/plugins/frontend/logger/client/logs.vue @@ -13,8 +13,6 @@ import { store, receive } from '~/client' import Converter from 'ansi_up' -receive('logs/data', data => store.logs.push(data)) - const hint = `app\u001b[0m \u001b[38;5;15;1mKoishi/` const converter = new Converter() diff --git a/plugins/frontend/logger/src/index.ts b/plugins/frontend/logger/src/index.ts index 2c706d43c5..12c3cf8381 100644 --- a/plugins/frontend/logger/src/index.ts +++ b/plugins/frontend/logger/src/index.ts @@ -91,7 +91,7 @@ class LogProvider extends DataSource { this.createFile() } this.writer.write(text) - this.ctx.console.broadcast('logs/data', text) + this.patch([text]) } async get() { diff --git a/plugins/frontend/manager/src/bots.ts b/plugins/frontend/manager/src/bots.ts index 8cfed55906..3aaea5bc8f 100644 --- a/plugins/frontend/manager/src/bots.ts +++ b/plugins/frontend/manager/src/bots.ts @@ -52,7 +52,7 @@ class BotProvider extends DataSource { }) ctx.on('bot-status-updated', () => { - this.broadcast() + this.refresh() }) } diff --git a/plugins/frontend/manager/src/market.ts b/plugins/frontend/manager/src/market.ts index a3d317e5ff..f5db746983 100644 --- a/plugins/frontend/manager/src/market.ts +++ b/plugins/frontend/manager/src/market.ts @@ -29,10 +29,11 @@ function supports(command: string, args: string[] = []) { } class MarketProvider extends DataSource> { - dataCache: Dict = {} - http: Quester - _timestamp = 0 - _agentCache: Promise + private http: Quester + private timestamp = 0 + private agentTask: Promise + private fullCache: Dict = {} + private tempCache: Dict = {} constructor(ctx: Context, private config: MarketProvider.Config) { super(ctx, 'market') @@ -52,9 +53,10 @@ class MarketProvider extends DataSource> { flushData() { const now = Date.now() - if (now - this._timestamp < 100) return - this._timestamp = now - this.broadcast() + if (now - this.timestamp < 100) return + this.timestamp = now + this.patch(this.tempCache) + this.tempCache = {} } private async search(offset = 0) { @@ -75,8 +77,8 @@ class MarketProvider extends DataSource> { const community = name.startsWith('koishi-plugin-') if (!official && !community) return - const data = await this.http.get(`/${name}`) - const versions = Object.values(data.versions).filter((remote) => { + const registry = await this.http.get(`/${name}`) + const versions = Object.values(registry.versions).filter((remote) => { const { dependencies, peerDependencies, deprecated } = remote const declaredVersion = { ...dependencies, ...peerDependencies }['koishi'] return !deprecated && declaredVersion && satisfies(currentVersion, declaredVersion) @@ -84,7 +86,7 @@ class MarketProvider extends DataSource> { if (!versions.length) return const shortname = official ? name.slice(17) : name.slice(14) - this.dataCache[name] = { + this.tempCache[name] = this.fullCache[name] = { ...item, shortname, official, @@ -103,7 +105,7 @@ class MarketProvider extends DataSource> { } async get() { - return this.dataCache + return this.fullCache } get cwd() { @@ -146,15 +148,15 @@ class MarketProvider extends DataSource> { } install = async (name: string) => { - const agent = await (this._agentCache ||= this.getAgent()) + const agent = await (this.agentTask ||= this.getAgent()) await this.exec(agent, [agent === 'yarn' ? 'add' : 'install', name, '--loglevel', 'error']) - this.ctx.console.services.packages.broadcast() + this.ctx.console.services.packages.refresh() } uninstall = async (name: string) => { - const agent = await (this._agentCache ||= this.getAgent()) + const agent = await (this.agentTask ||= this.getAgent()) await this.exec(agent, ['remove', name, '--loglevel', 'error']) - this.ctx.console.services.packages.broadcast() + this.ctx.console.services.packages.refresh() } } diff --git a/plugins/frontend/manager/src/packages.ts b/plugins/frontend/manager/src/packages.ts index cacc227a92..ce47c2342a 100644 --- a/plugins/frontend/manager/src/packages.ts +++ b/plugins/frontend/manager/src/packages.ts @@ -52,7 +52,7 @@ class PackageProvider extends DataSource> { if (!this.cache[entry]) return const local = await this.cache[entry] local.id = id - this.broadcast() + this.refresh() } async prepare() { @@ -121,7 +121,7 @@ class PackageProvider extends DataSource> { const oldLength = Object.keys(Adapter.library).length const exports = getExports(name) const newLength = Object.keys(Adapter.library).length - if (newLength > oldLength) this.ctx.console.services.protocols.broadcast() + if (newLength > oldLength) this.ctx.console.services.protocols.refresh() // check plugin dependencies Object.assign(result, Package.Meta.from(data)) diff --git a/plugins/frontend/manager/src/protocols.ts b/plugins/frontend/manager/src/protocols.ts index e483d71392..e214da7c6e 100644 --- a/plugins/frontend/manager/src/protocols.ts +++ b/plugins/frontend/manager/src/protocols.ts @@ -6,7 +6,7 @@ export default class AdapterProvider extends DataSource> { super(ctx, 'protocols') ctx.on('adapter', () => { - this.broadcast() + this.refresh() }) } diff --git a/plugins/frontend/manager/src/services.ts b/plugins/frontend/manager/src/services.ts index be089c7de9..de711133a5 100644 --- a/plugins/frontend/manager/src/services.ts +++ b/plugins/frontend/manager/src/services.ts @@ -2,18 +2,21 @@ import { DataSource } from '@koishijs/plugin-console' import { Context, Dict } from 'koishi' export default class ServiceProvider extends DataSource> { + private cache: Dict + constructor(ctx: Context) { super(ctx, 'services') - ctx.on('service', () => this.broadcast()) + ctx.on('service', () => this.refresh()) } - async get() { - const result: Dict = {} + async get(forced = false) { + if (!forced) return this.cache + this.cache = {} for (const name of Context.Services) { const value = this.ctx[name]?.['ctx']?.state.id - if (value) result[name] = value + if (value) this.cache[name] = value } - return result + return this.cache } } diff --git a/plugins/frontend/status/src/profile.ts b/plugins/frontend/status/src/profile.ts index d01d969c67..f416d842b7 100644 --- a/plugins/frontend/status/src/profile.ts +++ b/plugins/frontend/status/src/profile.ts @@ -52,7 +52,7 @@ export class ProfileProvider extends DataSource { ctx.on('ready', () => { ctx.setInterval(() => { updateCpuUsage() - this.broadcast() + this.refresh() }, tickInterval) }) }