Files
deepseek-harness/packages/util/plugin-detail/src/response.ts
T
turtle1999 9c8522bb64 feat(plugin-detail): show Markdown plugin details on bundle and market entry pages
Plugins bundle pages and Discover entry pages show the plugin's long
description first, as Markdown with images, in a height-limited region
without its own heading. The document is the file locale meta.detail
names, otherwise the package README, otherwise (market entries only)
the declared repository's README.

The new dsh-plugin-detail library selects documents and reads local
package directories and npm file CDNs without state. plugin-manager
reads installed and offered bundles through readBundleDetail; plugin-market
reads market entries through readEntryDetail, single-file APIs, and a
size-limited partial clone cache serialized across processes. Raw HTML is
omitted, relative links stay text, and relative images load from Host
routes with a sandbox CSP.
2026-10-19 19:13:22 +08:00

38 lines
1.8 KiB
TypeScript

/** HTTP responses of Host routes that serve images beside detail documents. */
import type { DetailFailureReason } from './types.ts'
/** An image read beside a detail document. */
export interface DetailImage {
readonly bytes: Uint8Array
readonly mediaType: string
}
/** HTTP status of each detail failure. */
const FAILURE_STATUS = {
'too-large': 413, 'network': 502, 'denied': 403, 'git-unavailable': 503, 'invalid': 400,
} as const satisfies Record<DetailFailureReason, number>
/**
* The response of an image route: the image, 404 when there is none, or the failure's status with its reason as the
* body. Every response carries `X-Content-Type-Options: nosniff` and a sandbox content security policy, because an SVG
* opened directly would otherwise run scripts on the authenticated API origin; only an image gets `cacheControl`.
* @param outcome - the image, undefined when none exists, or why it cannot be read; `invalid` also answers a malformed request.
* @param cacheControl - the `Cache-Control` value of an image.
* @returns the response.
*/
export function detailImageResponse(outcome: DetailImage | DetailFailureReason | undefined, cacheControl: string): Response {
const [status, body, type] = typeof outcome === 'object'
// A copy into a fresh buffer is a body `Response` accepts whatever buffer the bytes view.
? [200, new Uint8Array(outcome.bytes), outcome.mediaType]
: outcome === undefined ? [404, 'not found', undefined] : [FAILURE_STATUS[outcome], outcome, undefined]
return new Response(body, {
status,
headers: {
'Content-Type': type ?? 'text/plain; charset=utf-8',
'Cache-Control': status === 200 ? cacheControl : 'no-store',
'X-Content-Type-Options': 'nosniff',
'Content-Security-Policy': "sandbox; default-src 'none'; style-src 'unsafe-inline'",
},
})
}