Files
deepseek-harness/packages/boot/plugin-market/src/parse.ts
T
turtle1999 3de3a977e0 feat(plugin-market): add the Custom plugin markets bundle and Discover tab
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.
2026-10-15 05:37:44 +08:00

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 }
}
}