Files
deepseek-harness/apps/plugin-market-generator/src/sources.ts
T
turtle1999 434dc116f0 feat(plugin-market-generator): add a reference index generator
Generate a validated format-1 plugin market index from a list of npm package
names and https Git repository URLs, without running package code.

- List every non-deprecated npm bundle version, and Git versions at release
  tags and the default branch, each with its engines.dsh range.
- Read display fields, keywords, homepage, and icons the way DSH reads
  installed plugins; inline small icons up to the format's 32 KiB limit.
- Cache results per immutable input and rewrite output only when it changes.
2026-10-15 05:37:54 +08:00

49 lines
2.2 KiB
TypeScript

/** Read the source list: one npm package name or Git repository URL per line. */
import { parseMarketIndex, type MarketIndexError } from '@deepseek-ai/dsh-plugin-market-index'
/** One line of the source list. */
export type Source =
| { readonly kind: 'npm'; readonly line: string; readonly name: string }
| { readonly kind: 'git'; readonly line: string; readonly url: string }
/** Throw the reason the index format rejects a plugin with this name and source. */
function check(name: string, source: object): void {
const version = { version: '0.0.0', publishTimestamp: 0, source }
parseMarketIndex({ format: 1, title: 'check', plugins: [{ name, versions: [version] }] }, 'https://index.invalid/')
}
/**
* Parse a source list. Blank lines and lines starting with `#` are ignored; a trailing `/` or `.git` on a repository
* URL is dropped, so `https://github.com/o/r/` and `https://github.com/o/r.git` name the same repository. Every line
* must be a package name or a Git URL the index format accepts.
* @param text - the list file contents.
* @returns the sources in file order, without repeats.
* @throws when a line is neither an npm package name nor an https repository URL.
*/
export function parseSources(text: string): Source[] {
const sources: Source[] = []
const seen = new Set<string>()
for (const [index, raw] of text.split(/\r?\n/u).entries()) {
const line = raw.trim()
if (line === '' || line.startsWith('#')) continue
let source: Source
try {
if (line.includes('://')) {
const url = URL.canParse(line) ? new URL(line).href.replace(/\/+$/u, '').replace(/\.git$/u, '') : line
check('placeholder', { type: 'git', url, commit: '0'.repeat(40) })
source = { kind: 'git', line, url }
} else {
check(line, { type: 'npm' })
source = { kind: 'npm', line, name: line }
}
} catch (error) {
throw new Error(`line ${index + 1}: not an npm package name or an https Git repository URL (${(error as MarketIndexError).reason}): ${line}`)
}
const key = source.kind === 'npm' ? `npm:${source.name}` : `git:${source.url}`
if (seen.has(key)) continue
seen.add(key)
sources.push(source)
}
return sources
}