Read configured plugin market indexes on the Host, search them with fuzzy multilingual matching, and install a chosen version from the Plugins page. - dsh-plugin-market-index validates format-1 indexes and builds install arguments; inline icons are capped at 32 KiB. - The pluginMarket service fetches configured http(s) URLs, parses bodies in a Worker Thread, caches documents, and pages filtered, ordered results with installation status and engines.dsh compatibility. - Discover is an opt-in experimental bundle; the Plugins page gains a plugins.tab slot and confirmed installs. - The plugin_market tool lists indexes, plugins, and versions for Creator mode. - Plugin compatibility reads engines.dsh before DSH peer dependencies.
44 lines
1.8 KiB
TypeScript
44 lines
1.8 KiB
TypeScript
/** Decoding, JSON parsing, and format validation of a fetched index body; the parse Worker runs it. */
|
|
import { MarketIndexError, parseMarketIndex, type MarketIndex } from '@deepseek-ai/dsh-plugin-market-index'
|
|
|
|
/** A validated index, or the JSON path, rule, and diagnostic of the first violation; `$` covers UTF-8 and JSON errors. */
|
|
export type IndexParseResult =
|
|
| { readonly ok: true; readonly index: MarketIndex }
|
|
| { readonly ok: false; readonly path: string; readonly reason: string; readonly message: string }
|
|
|
|
/** The request a parse Worker receives as `workerData`. */
|
|
export interface IndexParseRequest {
|
|
readonly body: Uint8Array
|
|
readonly url: string
|
|
}
|
|
|
|
/**
|
|
* Decode a body as strict UTF-8, parse it as JSON, and validate it as format 1.
|
|
* @param body - the complete response body.
|
|
* @param url - the index URL relative icon references resolve against.
|
|
* @returns the validated index, or the first violation.
|
|
*/
|
|
export function parseIndexBody(body: Uint8Array, url: string): IndexParseResult {
|
|
let text: string
|
|
try {
|
|
text = new TextDecoder('utf-8', { fatal: true }).decode(body)
|
|
} catch {
|
|
// A fatal TextDecoder throws only for malformed UTF-8.
|
|
return { ok: false, path: '$', reason: 'must be UTF-8', message: 'must be UTF-8' }
|
|
}
|
|
let value: unknown
|
|
try {
|
|
value = JSON.parse(text)
|
|
} catch (error) {
|
|
const reason = `must be JSON: ${(error as Error).message}`
|
|
return { ok: false, path: '$', reason, message: reason }
|
|
}
|
|
try {
|
|
return { ok: true, index: parseMarketIndex(value, url) }
|
|
} catch (error) {
|
|
/* v8 ignore next -- parseMarketIndex throws nothing but its own validation failure */
|
|
if (!(error instanceof MarketIndexError)) throw error
|
|
return { ok: false, path: error.path, reason: error.reason, message: error.message }
|
|
}
|
|
}
|