@koishijs/plugin-market
Version:
Manage your bots and plugins with console
114 lines (102 loc) • 3.55 kB
text/typescript
import { Context, Dict, HTTP, Schema, Time } from 'koishi'
import Scanner, { SearchObject, SearchResult } from '@koishijs/registry'
import { MarketProvider as BaseMarketProvider } from '../shared'
class MarketProvider extends BaseMarketProvider {
private http: HTTP
private failed: string[] = []
private scanner: Scanner
private fullCache: Dict<SearchObject> = {}
private tempCache: Dict<SearchObject> = {}
private flushData: () => void
constructor(ctx: Context, public config: MarketProvider.Config) {
super(ctx)
if (config.endpoint) this.http = ctx.http.extend(config)
this.flushData = ctx.throttle(() => {
ctx.console.broadcast('market/patch', {
data: this.tempCache,
failed: this.failed.length,
total: this.scanner.total,
progress: this.scanner.progress,
})
this.tempCache = {}
}, 500)
}
async start(refresh = false) {
this.failed = []
this.fullCache = {}
this.tempCache = {}
if (refresh) this.ctx.installer.refresh(true)
await this.prepare()
super.start()
}
async collect() {
const { timeout } = this.config
const registry = this.ctx.installer.http
this.failed = []
this.scanner = new Scanner(registry.get)
if (this.http) {
const result = await this.http.get<SearchResult>('')
this.scanner.objects = result.objects.filter(object => !object.ignored)
this.scanner.total = this.scanner.objects.length
this.scanner.version = result.version
} else {
await this.scanner.collect({ timeout })
}
if (!this.scanner.version) {
this.scanner.analyze({
version: '4',
onFailure: (name, reason) => {
this.failed.push(name)
if (registry.config.endpoint.startsWith('https://registry.npmmirror.com')) {
if (this.ctx.http.isError(reason) && reason.response?.status === 404) {
// ignore 404 error for npmmirror
}
}
},
onRegistry: (registry, versions) => {
this.ctx.installer.setPackage(registry.name, versions)
},
onSuccess: (object, versions) => {
// npmmirror lacks `links` field
object.package.links ||= {
npm: `${registry.config.endpoint.replace('registry.', 'www.')}/package/${object.package.name}`,
}
this.fullCache[object.package.name] = this.tempCache[object.package.name] = object
},
after: () => this.flushData(),
})
}
return null
}
async get() {
await this.prepare()
if (this._error) return { data: {}, failed: 0, total: 0, progress: 0 }
return this.scanner.version ? {
registry: this.ctx.installer.endpoint,
data: Object.fromEntries(this.scanner.objects.map(item => [item.package.name, item])),
failed: 0,
total: this.scanner.total,
progress: this.scanner.total,
gravatar: process.env.GRAVATAR_MIRROR,
} : {
registry: this.ctx.installer.endpoint,
data: this.fullCache,
failed: this.failed.length,
total: this.scanner.total,
progress: this.scanner.progress,
gravatar: process.env.GRAVATAR_MIRROR,
}
}
}
namespace MarketProvider {
export interface Config {
endpoint?: string
timeout?: number
}
export const Config: Schema<Config> = Schema.object({
endpoint: Schema.string().role('link'),
timeout: Schema.number().role('time').default(Time.second * 30),
proxyAgent: Schema.string().role('link'),
})
}
export default MarketProvider